2010-07-21 12 views
7

Czytałem przewodnik po stylach w języku C++ i wprowadziłem zamieszanie w części Doing Work in Constructors. Jedną z wad robi ciężką pracę w konstruktora jest:Wątpliwość w jednej z wielu wad pracy wykonawców:

Jeśli praca wywołuje funkcje wirtualne, te rozmowy nie zostaną wysłane do implementacji podklasy. Przyszłość modyfikacja klasy może po cichu być wprowadzić ten problem, nawet jeśli Twoja klasa nie jest obecnie podklasowana, powodując wiele zamieszania.

Nie rozumiem, co to znaczy. Czy ktoś mógłby podać wyjaśnienie i dlaczego może to być uznane za wadę?

+2

Jest to jeden z powodów, dla których nie podoba mi się przewodnik w stylu C++ google. Wywoływanie wirtualnych metod z konstruktorów i destruktorów powinno być unikane jako jedna rzecz z reguły - gdziekolwiek tego nie robisz, to tylko zrób to, jeśli masz dobry powód i możesz go bronić w przeglądzie kodu. Z drugiej strony, zalecenie użycia dwuetapowej inicjalizacji jest (prawdopodobnie) całkowicie błędne, a wiele innych przewodników po stylach zaleca dwuetapowe inicjowanie ... –

+2

Pamiętaj, że przewodnik po stylu Google nie jest w C++, pomimo tytułu strony . Zakazują wyjątków, podając skandaliczny język, w którym wiele idiomów C++ albo nie może być użytych, albo pozbawionych ich użyteczności. Jest to powód, dla którego wymagają dwuetapowej inicjalizacji i wszystkich podatnych na błędy bólów głowy, które pociąga za sobą; nie ma możliwości wskazania awarii od konstruktora. Jeśli nie masz konkretnego powodu, by podążać za stylem Google, zignoruj ​​go. –

Odpowiedz

9

mam rażąco zgrywania wyłączyć niektóre przykładowy kod z Wikipedii Virtual function stronie:

#include <iostream> 
#include <vector> 

class Animal { 
    public: 
     virtual void eat() const { 
      std::cout << "I eat like a generic Animal." << std::endl; 
     } 
     virtual ~Animal() { 
     } 
}; 

class Wolf : public Animal { 
    public: 
     void eat() const { 
      std::cout << "I eat like a wolf!" << std::endl; 
     } 
}; 

class Fish : public Animal { 
    public: 
     void eat() const { 
      std::cout << "I eat like a fish!" << std::endl; 
     } 
}; 

Jeśli zadzwonisz eat() wewnątrz konstruktora Animal, to nazywamy Animaleat() funkcja za każdym razem. Nawet po utworzeniu obiektu Wolf lub Fish, ponieważ konstruktor Animal zostanie ukończony przed zainicjowaniem obiektu podklasy, nadrzędne funkcje eat nie będą jeszcze działać.

Jest to wadą, ponieważ może powodować zamieszanie między tym, co jest oczekiwane, a tym, co faktycznie się dzieje. Jeśli przesłonię eat, a następnie utworzę obiekt mojej podklasy, oczekuję, że moja nadpisana funkcja zostanie wywołana, nawet z odwołania do Animal. Spodziewam się tego, ponieważ tak się dzieje, gdy wywołanie jest wykonywane bezpośrednio przez kod poza konstruktorem. Zachowanie jest inne wewnątrz konstruktora, co powoduje, że drapię się po głowie w oszołomieniu.

+8

Nie ma "klasowej jaszczurki"? –

+2

@Justin: Nie mogę * wierzyć * przegapiłem tę okazję. ;) –

4

Podczas konstruowania obiektu, konstruktory dla klas bazowych są nazywane jako pierwsze. Ponieważ klasa pochodna nie została jeszcze zainicjalizowana, wszelkie wywołania metod wirtualnych podczas konstruktora klasy podstawowej nie będą miały obiektu klasy pochodnej do pracy.

0

Zadaniem konstruktora jest po prostu zainicjowanie stanu początkowego obiektu. Jeśli zaczniesz wykonywać zadania, które opierają się na akcjach podejmowanych w funkcjach wirtualnych, możesz uzyskać nieoczekiwane rezultaty, gdy podklasy zaczną implementować te funkcje.

1

Jeśli dziedziczysz z klasy, metody, które nadpisujesz/implementujesz, nie będą w tym przypadku wywoływane. Jeśli więc pracownik wywoła metodę work() w konstruktorze, to później wymyśli: Hourly :: work() i SalariedEmployee :: work(), które nie zostaną wywołane. Mimo że mają różne implementacje, nadal będą traktowane jako pracownicy, a nie ich specjalne implementacje.

2

Instancja podklasy jest utworzona, po pierwsze jest nazywane konstruktor klasy bazowej, wówczas konstruktor podklasy.

Jeśli konstruktor klasy podstawowej wywoła metodę wirtualną, wówczas metoda klasy bazowej zostanie wywołana zamiast podklasy, chociaż instancja jest instancją podklasy. To może być problem.

Dużo więcej informacji tutaj: http://www.artima.com/cppsource/nevercall.html

0

Zachowanie, które wywołania funkcji wirtualnej nie są wirtualne, jest dobrze zdefiniowane, ale bardzo zaskakujące, więc kompilatory zwykle emitują ostrzeżenia.

Powiązane problemy