W ten sposób można zademonstrować przykład, w którym wskaźnik do stałej ma duże zastosowanie. Załóżmy, że masz klasę z dynamiczną tablicą w środku i chcesz przekazać użytkownikowi dostęp do tablicy, ale bez przyznawania im praw do zmiany wskaźnika. Rozważać:
#include <new>
#include <string.h>
class TestA
{
private:
char *Array;
public:
TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
~TestA(){if(Array != NULL){ delete [] Array;} }
char * const GetArray(){ return Array; }
};
int main()
{
TestA Temp;
printf("%s\n",Temp.GetArray());
Temp.GetArray()[0] = ' '; //You can still modify the chars in the array, user has access
Temp.GetArray()[1] = ' ';
printf("%s\n",Temp.GetArray());
}
Który produkuje:
Dane wejściowe
wprowadzają dane
Ale jeśli spróbujemy tego:
int main()
{
TestA Temp;
printf("%s\n",Temp.GetArray());
Temp.GetArray()[0] = ' ';
Temp.GetArray()[1] = ' ';
printf("%s\n",Temp.GetArray());
Temp.GetArray() = NULL; //Bwuahahahaa attempt to set it to null
}
Otrzymujemy:
błąd: wartość l wymagana jako lewy operand przypisania // Znowu udaremniono Drat!
Więc oczywiście możemy zmodyfikować zawartość tablicy, ale nie wskaźnik tablicy. Dobrze, jeśli chcesz mieć pewność, że wskaźnik ma spójny stan podczas przekazywania go z powrotem do użytkownika. Jest jednak jeden haczyk:
int main()
{
TestA Temp;
printf("%s\n",Temp.GetArray());
Temp.GetArray()[0] = ' ';
Temp.GetArray()[1] = ' ';
printf("%s\n",Temp.GetArray());
delete [] Temp.GetArray(); //Bwuahaha this actually works!
}
Nadal możemy usunąć odwołanie do pamięci wskaźnika, nawet jeśli nie możemy zmodyfikować samego wskaźnika.
Więc jeśli chcesz, aby odwołanie do pamięci zawsze wskazywało na coś (IE nigdy nie może być modyfikowane, podobnie jak obecnie działa odniesienie), to jest wysoce przydatne. Jeśli chcesz, aby użytkownik miał pełny dostęp i mógł go modyfikować, to opcja non-const jest dla Ciebie.
Edytować:
Po zauważeniu komentarza okorz001 o niemożności przypisania ze względu na to, że GetArray () jest operandem o właściwej wartości, jego komentarz jest całkowicie poprawny, ale powyższe nadal ma zastosowanie, jeśli miałbyś zwrócić odniesienie do wskaźnika (przypuszczam, że założyłem, że GetArray jest odwołując się do referencji), na przykład:
class TestA
{
private:
char *Array;
public:
TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
~TestA(){if(Array != NULL){ delete [] Array;} }
char * const &GetArray(){ return Array; } //Note & reference operator
char * &GetNonConstArray(){ return Array; } //Note non-const
};
int main()
{
TestA Temp;
Temp.GetArray() = NULL; //Returns error
Temp.GetNonConstArray() = NULL; //Returns no error
}
Zwróci w pierwszym, powodując błąd:
błąd: przypisanie lokalizacji tylko do odczytu „Temp.TestA :: GetArray ()”
Ale druga nastąpi wesoło, pomimo potencjalnych konsekwencji pod spodem.
Oczywiście pojawi się pytanie „dlaczego miałbyś chcieć zwrócić odniesienie do wskaźnika”? Istnieją rzadkie przypadki, w których trzeba przypisać pamięć (lub dane) bezpośrednio do oryginalnego wskaźnika, o którym mowa (na przykład, zbudować własny interfejs malloc / free lub new / free), ale w tych przypadkach jest to odniesienie inne niż const . Odniesienie do wskaźnika const Nie spotkałem się z sytuacją, która by to uzasadniała (chyba że jako zadeklarowane zmienne referencyjne const, a nie typy zwracane?).
Zastanów się, czy mamy funkcję, która przyjmuje wskaźnik do stałej (w przeciwieństwie do takiej, która tego nie robi):
class TestA
{
private:
char *Array;
public:
TestA(){Array = NULL; Array = new (std::nothrow) char[20]; if(Array != NULL){ strcpy(Array,"Input data"); } }
~TestA(){if(Array != NULL){ delete [] Array;} }
char * const &GetArray(){ return Array; }
void ModifyArrayConst(char * const Data)
{
Data[1]; //This is okay, this refers to Data[1]
Data--; //Produces an error. Don't want to Decrement that.
printf("Const: %c\n",Data[1]);
}
void ModifyArrayNonConst(char * Data)
{
Data--; //Argh noo what are you doing?!
Data[1]; //This is actually the same as 'Data[0]' because it's relative to Data's position
printf("NonConst: %c\n",Data[1]);
}
};
int main()
{
TestA Temp;
Temp.ModifyArrayNonConst("ABCD");
Temp.ModifyArrayConst("ABCD");
}
Błąd w stałej daje w ten sposób komunikat:
błąd: zmniejszenie wartości parametru tylko do odczytu „Dane”
Co jest dobre, ponieważ prawdopodobnie nie chcemy tego robić, chyba że chcemy spowodować problemy opisane w komentarzach. Jeśli wyedytujemy dekrementację w funkcji const, wystąpią następujące zdarzenia:
NonConst: A
Const: B
Oczywiście, mimo że A to „Dane [1]”, jest traktowane jako „Dane [0]”, ponieważ wskaźnik NonConst zezwalał na operację zmniejszania. Po zaimplementowaniu const, jak pisze inna osoba, wychwytujemy potencjalny błąd, zanim się pojawi.
Inną ważną kwestią jest to, że wskaźnik do stałej może być użyty jako pseudo-odniesienie, ponieważ rzecz, na którą wskazuje odniesienie, nie może zostać zmieniona (można się zastanawiać, czy być może tak został zaimplementowany). Rozważać:
int main()
{
int A = 10;
int * const B = &A;
*B = 20; //This is permitted
printf("%d\n",A);
B = NULL; //This produces an error
}
Podczas próby kompilacji powoduje następujący błąd:
błąd: przypisanie zmiennej tylko do odczytu „B”
Co jest prawdopodobnie złą rzeczą, jeśli chodziło o ciągłe odniesienie do A. Jeśli B = NULL
zostanie zakomentowany, kompilator z radością pozwoli nam zmodyfikować, *B
a zatem A. Może to nie wydawać się przydatne w przypadku ints, ale zastanów się, czy miałeś jedną postawę aplikacji graficznej, w której chciałbyś niemodyfikowalnego wskaźnika, który odnosi się do niej, który mógłbyś przekazać na około.
Jego użycie jest zmienne (przepraszam za niezamierzony kalambur), ale używane poprawnie, jest to kolejne narzędzie w pudełku, które pomaga w programowaniu.