2011-01-21 17 views
24

Witam i dziękuję za przeczytanie. Może być po prostu objętych kategorii "osobistych preferencji, ale i tak jedziemy ...Definiowanie konstruktora w pliku nagłówkowym Plik implementacji VS (.cpp)

mogę zdefiniować ciało konstruktora klasy w klasie .h pliku lub w pliku .cpp realizacja . Te dwa style są prawdopodobnie identyczne, jeśli chodzi o kompilator w ramach konkretnego projektu (projekt dla mnie oznacza dll). To samo dotyczy wszystkich funkcji składowych naprawdę: można je zdefiniować w pliku nagłówkowym lub po prostu tam zadeklarować, a następnie zdefiniować w pliku cpp.

JEDNAK ...

Uważam, że jeśli trzeba dołączyć plik nagłówkowy takie klasa (-y) w różnych projektach (co oznacza, że ​​ostatecznie kod, który używa pliku nagłówka kończy się w innym dll) wtedy faktyczna implementacja w pliku nagłówkowym powoduje pewne bóle głowy podczas kompilacji (nie w łączeniu ... nawet nie dojdę do tego punktu). Dlaczego? No cóż, nie będę się zbytnio zajmował szczegółami, ale kompilator najwyraźniej próbuje rozwiązać wszystkie funkcje, które mogą być zdefiniowane w innych plikach nagłówkowych itp. ... zmuszając biednego programistę do rozpoczęcia pobierania różnych plików nagłówkowych ...

LONGSTORY short:

Czy nie zawsze najlepiej jest zachować pliki nagłówkowe wolne od implementacji i po prostu używać ich do "deklaracji"? Ułatwiłoby to włączenie ich do więcej niż jednego projektu bez konieczności noszenia dodatkowych śmieci.

Co sądzisz o tym? Dzięki!

Odpowiedz

21

Zachowaj nagłówki wolne od implementacji, chyba że chcesz, aby implementacje zostały wstawione (np. Trywialne moduły pobierające/ustawiające). A jeśli nie są szablonami, oczywiście.

Nie widzę powodu, aby zrobić wyjątek dla konstruktorów. Umieść je w pliku .cpp.

+1

Istnieją nawet przypadki, w których można umieścić implementację w nagłówku. Powiedzmy, że jeśli dwa różne pliki muszą się wzajemnie uzupełniać, nie można tego zrobić w nagłówku, należy to zrobić na stronie cpp. –

+0

Masz na myśli "nie może", a nie "może". W przeciwnym razie, dobry punkt. – Thomas

+2

Wyjątek "trywialny" można równie łatwo zastosować do trywialnego konstruktora. Nie ma powodu, aby traktować je inaczej lub nie mieć ich również w formie inline. –

17

Należy zwrócić uwagę na to, że jeśli w pliku nagłówkowym zdefiniowana jest funkcja składowa, musi ona znajdować się w treści klasy lub musi być wyraźnie oznaczona jako inline. Innymi słowy, jest to bzdura, aby zrobić to w pliku nagłówka:

class A { 
    public: 
    A(); 
}; 

A::A() { 
    // constructor body 
} 

Powodem jest zły dlatego, że uczyni to kompilator zawierają definicji w każdej jednostce kompilacji, gdy jest oczywiste, że każda funkcja musi być zdefiniowane tylko raz. Oto poprawne sposoby na to samo:

class A { 
    public: 
    inline A(); 
}; 

inline A::A() { 
    // constructor body 
} 

Lub:

class A { 
    public: 
    inline A() { // inline isn't required here, but it's a good style 
    // constructor body 
    } 
}; 

W obu przypadkach konstruktor jest inline. Jedynym poprawnym sposobem uczynienia go regularną funkcją out-of-line byłoby zdefiniowanie go w pliku implementacji, a nie w nagłówku. Jest to najważniejsza różnica między tymi dwoma podejściami.

Teraz warto zauważyć, że inline to optymalizacja. I jak zawsze przy optymalizacji, najlepiej ich unikać, dopóki nie okaże się to konieczne.Pośród innych problemów, które może powodować wprowadzanie linków, występuje problem kompatybilności: jeśli zmienisz treść funkcji, która nie została wstawiona, musisz tylko dokonać rekompilacji jednostki, w której jest ona zdefiniowana, i wszyscy zaczynają od razu korzystać z nowej implementacji. Dzięki funkcjom wstawionym musisz przekompilować każdą jednostkę, która zawiera odpowiedni nagłówek, co może być uciążliwe, szczególnie jeśli nagłówek jest używany w różnych projektach przez różne osoby.

Innymi słowy, używaj regularnych definicji out-of-line tam, gdzie to możliwe, dopóki nie zostanie udowodnione przez profilowanie, że określone wywołanie funkcji jest wąskim gardłem wydajności. Jedynym rozsądnym wyjątkiem od tej reguły są trywialne ustawiacze i pobierające, a nawet z nimi lepiej jest zachować ostrożność - pewnego dnia mogą one stać się nietrywialne i będzie to oznaczać wiele problemów z rekompilacją i kompatybilnością.

1

Kolejna uwaga do rozważenia: wszelkie zmiany w pliku nagłówkowym wymagają przebudowania wszystkich plików, które zawierają ten plik nagłówkowy. Większość systemów kompilacji przebudowuje pliki źródłowe (* .cpp/.cc), które zależą od zmodyfikowanego pliku nagłówkowego.

Po zmianie metody klasy zdefiniowanej w pliku nagłówkowym wszystkie pliki źródłowe, w tym plik nagłówkowy, zostaną przebudowane. Jeśli zmienisz metodę w pliku źródłowym, tylko plik źródłowy zostanie przebudowany. Może to być problem dla średnich i większych projektów.

Aby uprościć proces kompilacji, większość metod klasy należy zdefiniować w pliku źródłowym. W pliku nagłówkowym należy zdefiniować małe metody i inne kandydatury do wstawiania.