Wstęp:C++ 11 Usuń przesłonięta metoda
To jest pytanie o najlepsze praktyki dotyczące nowego znaczenia usuwania operatora wprowadzonego z C++ 11, gdy stosowane do klasy dziecięcej nadrzędnymi dziedziczną rodziców wirtualny metoda.
Tło:
Per standardu, pierwszy przypadek użycia cytowany jest wyraźnie zabronić funkcji połączeń dla niektórych rodzajów gdzie konwersje w przeciwnym razie być dorozumiane, takich jak przykładowo z §8.4.3 na ostatni C++11 standard draft:
struct sometype {
sometype() = delete; // OK, but redundant
some_type(std::intmax_t) = delete;
some_type(double);
};
powyższy przykład jest jasne i celowe. Jednak w poniższym przykładzie, w którym nowy operator został nadpisany i uniemożliwiony został wywołany, definiując go jako usunięty, zacząłem myśleć o innych scenariuszach, które później zidentyfikowałem w sekcji pytania (przykład poniżej jest z §8.4.3 z C++11 standard draft):
struct sometype {
void *operator new(std::size_t) = delete;
void *operator new[](std::size_t) = delete;
};
sometype *p = new sometype; // error, deleted class operator new
sometype *q = new sometype[3]; // error, deleted class operator new[]
Pytanie:
Przez rozszerzenie tej myśli do dziedziczenia, jestem ciekaw innych przemyśleń dotyczących tego, czy Poniższy przykład użycie jest jasne i ważne use-case lub jeśli jest to jasne nadużycie nowo dodanej funkcji. Proszę uzasadnić swoją odpowiedź (zostanie przyjęty przykład zapewniający najbardziej przekonujące uzasadnienie). W poniższym przykładzie projekt próbuje utrzymać dwie wersje biblioteki (wymagana jest instancja biblioteki), ponieważ druga wersja biblioteki dziedziczy po raz pierwszy. Chodzi o to, aby poprawki błędów lub zmiany wprowadzone w pierwszej wersji biblioteki były automatycznie propagowane do drugiej wersji biblioteki, pozwalając drugiej wersji biblioteki skupić się tylko na jej różnicach od pierwszej wersji. Deprecate funkcję w drugiej wersji biblioteki, operator delete jest używany, aby uniemożliwić połączenie do zastąpionej funkcję:
class LibraryVersion1 {
public:
virtual void doSomething1() { /* does something */ }
// many other library methods
virtual void doSomethingN() { /* does something else */ }
};
class LibraryVersion2 : public LibraryVersion1 {
public:
// Deprecate the doSomething1 method by disallowing it from being called
virtual void doSomething1() override = delete;
// Add new method definitions
virtual void doSomethingElse() { /* does something else */ }
};
Choć widzę wiele korzyści takiego podejścia, myślę, że wydają się bardziej ku myśli że jest to nadużycie tej funkcji. Podstawową pułapką, jaką widzę w powyższym przykładzie, jest to, że klasyczna relacja dziedziczenia "jest-a" jest zerwana. Przeczytałem wiele artykułów, które zdecydowanie zalecają przeciwdziałanie dziedziczeniu w celu wyrażenia relacji "sort-of-a-a", a zamiast tego użycie kompozycji z funkcjami wrappera, aby wyraźnie zidentyfikować relacje klas. Podczas gdy poniższy często marszczący się przykład wymaga więcej wysiłku w celu wdrożenia i utrzymania (w odniesieniu do liczby linii napisanych dla tego fragmentu kodu, ponieważ każda dziedziczna funkcja, która ma być publicznie dostępna musi być jawnie wywołana przez klasę dziedziczącą), użycie od delete jak przedstawiono powyżej, jest bardzo podobny pod wieloma względami:
class LibraryVersion1 {
public:
virtual void doSomething1() { /* does something */ }
virtual void doSomething2() { /* does something */ }
// many other library methods
virtual void doSomethingN() { /* does something */ }
};
class LibraryVersion2 : private LibraryVersion1 {
// doSomething1 is inherited privately so other classes cannot call it
public:
// Explicitly state which functions have not been deprecated
using LibraryVersion1::doSomething2();
// ... using (many other library methods)
using LibraryVersion1::doSomethingN();
// Add new method definitions
virtual void doSomethingElse() { /* does something else */ }
};
z góry dziękuję za odpowiedzi i dalszego wglądu do tego potencjalnego użytkowej przypadku kasowania.
Nie uważam, że 'virtual void doSomething1() override = delete;' jest legalne. Co byś miał '((LibraryVersion1 *) (new LibraryVersion2)) -> doSomething1()' zrobić? –
Według mojego zrozumienia, nawet z rzutowaniem na LibraryVersion1, usunięta funkcja wciąż próbowałaby zostać wywołana, biorąc pod uwagę zastąpienie przez LibraryVersion2 i spowodować, że kod się nie skompilował. W tym miejscu relacja "jest-a" jest zepsuta, jak zauważono w moim pytaniu, ale z pewnością wymusiłaby deprecjację zgodnie z przeznaczeniem. – statueuphemism
fresh: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3485.pdf – qPCR4vir