2010-10-08 20 views
26

Przepraszam, jeśli zostało to zadane, ale w jaki sposób utworzyć funkcję składową w języku C++, która zwraca wskaźnik w następujących sceneriach: 1. Zwrócony wskaźnik jest stały, ale zawartość wewnątrz może być modyfikowana. 2. Złom w środku jest stały, ale zwracany wskaźnik można zmodyfikować. 3. Ani śmieci, ani wskaźnik nie mogą być modyfikowane.Funkcja składowa C++ const, która zwraca wskaźnik const. Ale jaki typ const jest zwracanym wskaźnikiem?

Czy to tak:

  1. int *const func() const
  2. const int* func() const
  3. const int * const func() const

Wszystkie samouczki czytałem nie obejmują tego rozróżnienia.

Nota boczna: Nota boczna: Jeśli moja metoda jest zadeklarowana jako const, samouczki mówią, że stwierdzam, że nie zmienię parametrów. Ale nie jest to dla mnie wystarczająco jasne w przypadku, gdy parametr jest wskaźnikiem . Czy moje parametry muszą być takie jak:

a. void func(const int* const x) const;
b. void func(const int* x) const;
c. void func(const int* const x) const;

+0

sobie sprawę, że const funcs członkowskie mają nic wspólnego z parametrów modyfikujących, ale o modyfikację zmiennych klasy. – Jor

Odpowiedz

53

nie wiem co książka, którą przeczytałem, ale jeśli oznaczyć metoda const oznacza to, że this będzie od rodzaju const MyClass* zamiast MyClass*, co z kolei oznacza, że ​​nie można zmienić nonstatic członków danych, które są nie został zadeklarowany jako mutable, nie można też wywoływać żadnych niestałych metod na this.

Teraz dla wartości zwracanej.

1. int * const func() const

Funkcja jest stała, a zwracany wskaźnik jest stały, ale można zmienić "śmieci wewnątrz". Jednak nie widzę sensu zwracanie wskaźnika const, ponieważ ostateczne wywołanie funkcji będzie rwartością, a wartości rin nieklasowane nie mogą być const, co oznacza, że ​​const zostanie zignorowane i tak zostanie zignorowane. const int* func() const

Jest to przydatna rzecz. "Śmieci w środku" nie można modyfikować:

3. const int * const func() const

semantycznie prawie takie same jak 2, z przyczyn w 1.

HTH

+1

Myślę, że masz kilka przydatnych informacji w tej odpowiedzi, ale moje pytanie nadal pozostaje .. Jeśli zrobię const element członkowski, co ja * wymagane * mieć jako sygnatury mojej metody. Przepraszam, jeśli cię nie rozumiem. – Jor

+1

@Jor: Nie jesteś zobowiązany do żadnych ograniczeń dotyczących reszty podpisu funkcji. Jedyną rzeczą, na którą należy zwrócić uwagę, jest to, że w metodzie "const" to jest wskaźnikiem do 'const', więc jeśli zwrócisz element przez wskaźnik lub referencję, albo wartość zwracana będzie musiała być referencją, albo wskaźnikiem do typu' const' lub musiałbyś zrobić potencjalnie niebezpieczny "const_cast". –

+1

@Jor: Co oznacza, że ​​typ zwrotu zależy od tego, co powracasz. Jeśli zwracasz adres członka klasy, to ten członek jest const w ramach funkcji składowej stałej, więc chcesz opcję (2). –

9

Niektóre zastosowania const naprawdę nie ma większego sensu.

Załóżmy, że mamy następującą funkcję:

void myFunction (const int value); 

const informuje kompilator, że wartość ta nie musi zmieniać wewnątrz funkcji. Ta informacja nie ma żadnej wartości dla dzwoniącego.Od funkcji zależy decyzja co zrobić z wartością. Dla rozmówcy, dwa następujące definicje funkcji zachowują się dokładnie tak samo dla niego:

void myFunction (const int value); 
void myFunction (int value); 

Ponieważ wartość jest przekazywane przez wartość, co oznacza, że ​​funkcja dostaje lokalną kopię tak.

Z drugiej strony, jeśli argument jest odniesieniem lub wskaźnikiem, rzeczy stają się bardzo różne.

void myFunction (const MyClass &value); 

Mówi rozmówcę, że wartości przekazywane przez referencję (tak za ekranach to faktycznie wskaźnik), ale rozmówca nie zapowiada, aby zmienić wartość. To samo odnosi się do wskaźników:

void myFunction (const MyClass *value); 

Mijamy wskaźnik do MojaKlasa (ze względów wydajności), ale funkcja nie zapowiada, aby zmienić wartość.

Jeśli chcemy napisać następujące:

void myFunction (MyClass * const value); 

Potem wracamy int raz pierwszy sytuacja. myFunction pobiera wskaźnik, który jest przekazywany przez wartość, a który jest const. Ponieważ funkcja MyFunction pobiera kopię wartości wskaźnika, nie ma znaczenia dla wywołującego, czy jest ona stała, czy nie. Najważniejszą rzeczą jest to, że moja funkcja może zmieniać zawartość wartości, ponieważ sama zmienna wskaźnika jest stała, ale zawartość w niej nie jest.

To samo odnosi się do wartości zwracanej:

const double squareRoot(double d); 

To nie ma żadnego sensu. squareRoot zwraca const double, ale ponieważ jest przekazywana "według wartości", a zatem musi być skopiowana do mojej własnej zmiennej lokalnej, mogę zrobić, co zechcę.

Z drugiej strony:

const Customer *getCustomer(char *name); 

mi mówi, że mnie getCustomer zwraca wskaźnik do klienta, a nie wolno mi zmienić zawartość klienta.

Rzeczywiście, byłoby lepiej, aby const char-pointer-zawartości, jak również, ponieważ nie spodziewam funkcja zmienić dany ciąg:

const Customer *getCustomer(const char *name); 
+0

Dobra, zaczynam rozumieć Patricka i zdałem sobie sprawę, że metoda const ma związek z nie modyfikowaniem członków klasy. Więc zapomnij o tym. Ale podoba mi się twoja odpowiedź, więc może odpowiesz na te pytania: "const Customer * getCustomer (char * name);" powiedziałeś, że dzwoniący nie może zmodyfikować wyniku. Ale czy to nie jest tak, że nie mogą zmodyfikować wyniku za pomocą zwróconej zmiennej, ale jeśli skopiują ten wskaźnik do innego wskaźnika, to * mogą * modyfikować zawartość? – Jor

+0

Oto kolejne pytanie. Metoda const nie może zwrócić wskaźnika do jej elementów klasy, chyba że jest wskaźnikiem const. Ale jaki to musi być wskaźnik const? 1: const * int 2: const * const int 3: int * const – Jor

+0

Jeśli przypiszesz wartość zwracaną przez funkcję "const Customer * getCustomer()" do zmiennej "Customer *", wtedy będziesz mógł zmienić zawartość klienta. Jednak kompilator C++ podaje błąd w tym zadaniu, ponieważ zwykle nie można przypisać stałego wskaźnika do wskaźnika stałego, ponieważ przyznaje to "dodatkowe prawa" (a mianowicie: możliwość zmiany zawartości Klient). Jednak w niektórych przypadkach (szczególnie w przypadku starszego czystego kodu C) może być konieczne obejście tego, a następnie należy użyć const_cast. – Patrick

0

Metoda const uniemożliwia modyfikację członkowie. W przypadku wskaźników oznacza to, że nie można ponownie przypisać wskaźnika. Możesz modyfikować obiekt wskazywany przez wskaźnik do pożądania twojego serca.

Gdy wskaźnik jest zwracany przez wartość (kopia), osoba wywołująca nie może go użyć do zmodyfikowania elementu wskaźnika klasy. Dlatego dodanie wartości const do wartości zwracanej nie dodaje nic.

Rzeczy są różne, jeśli zwrócono referencję do wskaźnika. Teraz, jeśli wskaźnik nie byłby stały, oznaczałoby to, że funkcja, która nie ma uprawnień do modyfikowania wartości, przyznaje to prawo dzwoniącemu.

Przykład:

class X 
{ 
    int* p; 
public: 
    int* get_copy_of_pointer() const //the returned value is a copy of this->p 
    { 
     *p = 42; //this being const doesn't mean that you can't modify the pointee 
     //p = 0; //it means you can't modify the pointer's value 
     return p; 
    } 
    int* const& get_reference_to_pointer() const //can't return a reference to non-const pointer 
    { 
     return p; 
    } 
}; 
1

Wracając wskaźnik do const sprawia, że ​​wiele sensu, ale wracając const wskaźnik (nie można modyfikować) zwykle dodaje żadnej wartości (choć niektórzy twierdzą, że może to zapobiec błędom użytkownika lub dodać kompilator optymalizacja).

Dzieje się tak dlatego, że zwracana wartość należy do osoby wywołującej funkcję, tj. Jest to ich własna kopia, więc nie ma znaczenia, czy ją modyfikują (wskazać na coś innego). Treść jednak nie "należy" do osoby dzwoniącej, a osoba realizująca funkcję może zawrzeć umowę, że jest to informacja tylko do odczytu.

Funkcje członków Const nie obiecują zmiany stanu klasy, chociaż niekoniecznie jest to wymuszane w rzeczywistości przez kompilator. Nie odwołuję się tutaj do const_cast ani do zmiennego członkostwa tak bardzo jak do faktu, że jeśli twoja klasa zawiera wskazówki lub referencje, funkcja z elementem const zamienia twoje wskaźniki w stałe wskaźniki, ale nie czyni ich wskaźnikami do const, podobnie twoje referencje nie są obracane do referencji do stałej. Jeśli są to składniki twojej klasy (a takie komponenty są często reprezentowane przez wskaźniki), twoje funkcje mogą zmienić ich stan.

Zmutowani członkowie są po to, aby pozwolić klasie na ich zmianę bez zmiany stanu wewnętrznego. Zwykle można je zastosować do:

  • Muteksy, które chcesz zablokować nawet do czytania.
  • Dane leniwej ładowane, tj. Wypełniane przy pierwszym dostępie do nich.
  • Obiekty zliczane odsyłającymi: Użytkownik chce zwiększyć liczbę odwołań, jeśli ma inną przeglądarkę, w ten sposób modyfikuje jej stan, aby ją odczytać.

const_cast jest powszechnie uważany za "hack" i często jest robiony, gdy ktoś inny nie napisał poprawnie swojego kodu. Może mieć wartość chociaż w następujących sytuacjach:

  • Wiele przeciążenia gdzie jeden jest const i jeden non-const i const zwraca const referencję i non-const zwraca const odniesienia, ale inaczej oni są tacy sami. Duplikowanie kodu (jeśli nie jest to proste pobranie elementu danych) nie jest świetnym pomysłem, więc implementuj jeden pod względem drugiego i użyj const_cast, aby ominąć kompilator.

  • Tam, gdzie chcesz w szczególności wywołać przeciążenie const, ale nie odwołuj się do stałych. Najpierw prześlij go na const.

4

int *const func() const

Nie można obserwować const tutaj wyjątkiem kilku przypadków

  • Pobieranie adresu func.
  • W C++ 0x, bezpośrednie wywołanie func ze składnią wywołania funkcji jako operandem decltype, da int * const.

Dzieje się tak dlatego, że zwracana jest czysta wartość wskaźnika, czyli wartość wskaźnika, która nie jest przechowywana w zmiennej wskaźnika.Takie wartości nie mają stałych wartości, ponieważ i tak nie można ich zmienić. Nie możesz powiedzieć, obj.func() = NULL;, nawet jeśli odbierzesz const. W obu przypadkach wyrażenie obj.func() ma typ typu int* i nie można go modyfikować (ktoś wkrótce zacytuje standard i wymyśli termin "rvalue").

Więc w kontekstach, w których używasz wartości zwracanej, nie będziesz w stanie określić różnicy. Tylko w przypadkach, w których odwołujesz się do deklaracji lub samej funkcji, zauważysz różnicę.

const int* func() const

To jest to, czego zwykle zrobi jeśli ciało byłoby coś podobnego return &this->intmember;. Nie pozwala na zmianę elementu int przez wykonanie *obj.func() = 42;.

const int * const func() const

To właśnie połączenie tych dwóch pierwszych :)

Powiązane problemy