2015-07-08 52 views
6

Myślałem teoretycznie, że odpowiedź na to pytanie była twierdząca.Czy klasa pochodna może stać się uncopyable przez deklarowanie konstruktora kopiowania/operatora jako prywatną w klasie bazowej?

Jednak w praktyce mój kompilator (VS2010) nie wydaje się narzekać w następującej sytuacji: Mam abstrakcyjną klasę bazową zapewniającą pewien wspólny interfejs (ale nie mający żadnych członków danych) oraz różne subkategorie i pochodne od nich wyprowadzone.

class Base 
{ 
public: 
    Base() {} 
    virtual ~Base() {} 

    virtual void interfaceFunction1() = 0; 
    virtual void interfaceFunction2() = 0; 
private: 
    Base(const Base&);   // all derived classes should be uncopyable 
    Base& operator=(const Base&); 

    // no data members 
}; 

Mój kompilator stwierdził, że nawet implementowanie konstruktorów pełnych kopii w sub-subub klasach jest bezproblemowe.

Jak mogę się upewnić, że każda klasa wywodząca się z bazy jest niekopią?

edit: Jeśli dobrze rozumiem, to jest dokładnie to, co Scott Meyers wyjaśniono w pkt 6 Effective C++ (3rd edition, 2005) z jego ideą klasy Uncopyable (tylko rozszerzony tutaj, aby uzyskać pełną klasy interfejsu). Jaka jest różnica, która sprawia, że ​​jego pomysł działa? (Wiem, że dziedziczy on prywatnie, ale to nie powinno stanowić problemu)

+0

Może zostać dodany nieaktywny element danych. –

+1

Czy testowałeś inne kompilatory? Również prezentacja klasy pochodnej byłaby miła;) (a właściwie coś podobnego do kompilowanego przykładu.) – luk32

+0

Jaki jest pożytek z czystej funkcji, która nie jest oznaczona jako wirtualna? –

Odpowiedz

3

To powinno uniemożliwić kompilatorowi generowanie konstruktora kopiowania dla klas pochodnych, które nie deklarują jednoznacznie. Jednak nic nie stoi na przeszkodzie, aby klasa pochodna jawnie deklarowała konstruktora kopii, który zrobi coś innego niż wywołanie konstruktora kopiowania z Base.

Nie ma sposobu, aby upewnić się, że klasy pochodne są natychmiastowe, ale nie można ich kopiować.

+0

Może być rozwiązaniem, aby baza() była prywatna? (ale wtedy musiałbym uciekać się do statycznych metod fabrycznych, co brzmi jak wiele kłopotów tylko po to, by uczynić te klasy nieopublikalnymi). –

+0

@TG_ Nie, to by nie działało. Jeśli zostawisz * all * 'Base' konstruktorów' private', klasa pochodna nie będzie w stanie sama się zainicjować (nie przez kopiowanie, nie domyślnie, nie w inny sposób).Jeśli pozostawisz dostępny konstruktor 'Base', klasa pochodna może użyć tego w swoim własnym kodzie ctor. – Angew

1

Zamiast deklarować konstruktora kopiarki/operatora jako prywatne zadeklarować je jako usunięte. Zgłoszenie konstruktora/operatora kopiowania jako prywatnego nie jest najlepszym rozwiązaniem, które uniemożliwi kopiowanie klas pochodnych. Jeśli chcesz, aby klasa podstawowa była całkowicie niekopiowalna, zadeklaruj konstruktora/operatora kopiowania jakod, ponieważ kopia nadal może się odbywać wewnątrz funkcji składowych Base, ponieważ członkowie prywatni są dostępni dla funkcji tej klasy. Można użyć C++ 11 cechę Usuń:

Base(const Base&) = delete; // copy constructor 
Base& operator=(const Base&) = delete; // copy-assignment operator 

Ale uznającą konstruktora kopia/operatora jako prywatnej również prawo tak długo, jak jesteś świadomy, że kopia może nadal odbywać się wewnątrz funkcji członkowskich Podstawa.

+1

uczynienie ich prywatnymi i zadeklarowanie jako usuniętych ma taki sam efekt. –

+0

@ BЈовић Niekoniecznie, uczynienie ich prywatnymi nie gwarantuje, że obiekty tego typu nie będą mogły być kopiowane. Jeśli uznamy je za usunięte, żadna kopia nie może mieć miejsca. Element 11 Effective Modern C++ stwierdza, że ​​kod będący w funkcjach członków i znajomych nie powiedzie się, jeśli spróbuje użyć funkcji usuniętego członka. Zgłaszanie ich jako prywatnych oznacza, że ​​kod wewnątrz funkcji członka i przyjaciele danej klasy mogą z nich korzystać, a połączenie zakończy się niepowodzeniem z powodu braku definicji funkcji. –

+1

Domyślny konstruktor kopiowania utworzy nowy obiekt podstawowy, a nie spróbuje go skopiować. Tak więc to podejście nie zadziała, jeśli klasa pochodna używa domyślnego konstruktora kopiowania, a klasa pochodna będzie kopiowalna, podczas gdy baza nie. –