Często dobrym pomysłem jest posiadanie abstrakcyjnej klasy bazowej do izolowania interfejsu obiektu.
Problem polega na tym, że konstrukcja kopiowania, IMHO, jest domyślnie dość zepsuta w C ++, przy czym domyślnie generowane są konstruktory kopiowania.
Więc jakie są problemy, kiedy masz abstrakcyjną klasę bazową i surowe wskaźniki w klasach pochodnych?
class IAbstract
{
~IAbstract() = 0;
}
class Derived : public IAbstract
{
char *theProblem;
...
}
IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???
A teraz czy całkowicie wyłączasz tworzenie kopii dla całej hierarchii? Czy deklarujesz budowę kopii jako prywatną w IAbstract
?
Czy są jakieś trzy reguły z abstrakcyjnymi klasami podstawowymi?
c++
abstract-class
Koder
źródło
źródło
Odpowiedzi:
Kopiowanie konstrukcji klasy abstrakcyjnej powinno być w większości przypadków prywatne, a także operatora przypisania.
Klasy abstrakcyjne są, z definicji, typem polimorficznym. Nie wiesz więc, ile pamięci używa Twoja instancja, więc nie możesz jej bezpiecznie skopiować ani przypisać. W praktyce ryzykujesz krojenie: /programming/274626/what-is-the-slicing-problem-in-c
Typu polimorficznego w C ++ nie można manipulować wartością. Manipulujesz nimi za pomocą odwołania lub wskaźnika (lub dowolnego inteligentnego wskaźnika).
To jest powód, dla którego Java sprawiła, że obiektami można manipulować tylko przez odniesienie, i dlaczego C # i D mają separację między klasami i strukturami (pierwsza z nich jest polimorficzna i typem referencyjnym, druga nie jest polimorficzna i typem wartości).
źródło
Oczywiście możesz po prostu uczynić go chronionym i pustym, aby klasy pochodne mogły wybierać. Jednak bardziej ogólnie, twój kod jest i tak zabroniony, ponieważ nie można go utworzyć
IAbstract
- ponieważ ma czystą funkcję wirtualną. W związku z tym nie stanowi to problemu - twoich klas interfejsów nie można utworzyć i dlatego nie można ich skopiować, a bardziej pochodne klasy mogą blokować lub kontynuować kopiowanie, jak chcą.źródło
operator=(const Derived2&)
inDerived1
.Ustawiając ctor i przypisanie na prywatne (lub deklarując je jako = usuń w C ++ 11) wyłączasz kopiowanie.
Chodzi o to, GDZIE musisz to zrobić. Aby zachować kod, IAbstract nie stanowi problemu. (zwróć uwagę, że robiąc to, co zrobiłeś, przypisujesz
*a1
IAbstract
podobiekt do a2, tracąc wszelkie odniesienia doDerived
. Przypisanie wartości nie jest polimorficzne)Problem pochodzi z
Derived::theproblem
. Skopiowanie pochodnej do innej może w rzeczywistości współdzielić*theproblem
dane, które mogą nie być przeznaczone do współdzielenia (istnieją dwa przypadki, które mogą wywoływaćdelete theproblem
ich destruktor).W takim przypadku
Derived
musi to być niemożliwe do skopiowania i nieprzenoszalne. Oczywiście, jeśli ustawisz kopię jako prywatnąIAbstract
, ponieważ domyślna kopia tegoDerived
potrzebuje,Derived
również nie będzie można jej skopiować . Ale jeśli zdefiniujesz własneDerived::Derived(const Derived&)
bez wywoływaniaIAbtract
kopiowania, nadal możesz je skopiować.Problem leży w pochodnej, a rozwiązanie musi pozostać w pochodnej: jeśli musi to być obiekt tylko dynamiczny, do którego dostęp mają tylko wskaźniki lub referencje, to sama pochodna musi mieć
Zasadniczo to od projektanta klasy Derived (która powinna wiedzieć, jak działa Derived i jak
theproblem
zarządzać) decyduje, co zrobić z przypisaniem i kopiowaniem.źródło