Różnica między implementacją klasy wewnątrz pliku. h lub w a.cpp plik

Zastanawiałem się, jakie są różnice między deklarowaniem i implementacją klasy wyłącznie w pliku nagłówkowym, w porównaniu z normalnym podejściem, w którym można wstawić klasę w nagłówku i zaimplementować efektywnie .plik cpp.

Aby lepiej wyjaśnić, o czym mówię, mam na myśli różnice między normalnym podejściem:

// File class.h
class MyClass 
{
private:
  //attributes
  public:
  void method1(...);
  void method2(...);
  ...
};

//file class.cpp
#include "class.h"

void MyClass::method1(...) 
{
  //implementation
}

void MyClass::method2(...) 
{
  //implementation
}

And a just-header approach:

// File class.h
class MyClass 
{
private:
  //attributes
public:
  void method1(...) 
  {
      //implementation
  }

  void method2(...) 
  {
    //implementation
  }

  ...
};

Mogę uzyskać główną różnicę: w drugim przypadku kod jest zawarty w każdym innym pliku, który wymaga generowania większej liczby instancji tych samych implementacji, a więc implicit redundancy; podczas gdy w pierwszym przypadku kod jest kompilowany przez siebie, a następnie każde wywołanie, o którym mowa w obiekcie MyClass, jest powiązane z implementacją w class.cpp.

Ale czy są inne różnice? Czy wygodniej jest zastosować podejście zamiast innego w zależności od sytuacji? Gdzieś też czytałem, że definiowanie ciała metody bezpośrednio w pliku nagłówkowym jest niejawnym żądaniem do kompilatora, aby czy to prawda?

Author: Jack, 2009-11-27

6 answers

Główna praktyczna różnica polega na tym, że jeśli definicje funkcji członowych znajdują się w treści nagłówka, to oczywiście są one kompilowane raz dla każdej jednostki tłumaczenia, która zawiera ten nagłówek. Gdy twój projekt zawiera kilkaset lub tysiące plików źródłowych, a dana klasa jest dość szeroko stosowana, może to oznaczać wiele powtórzeń. Nawet jeśli każda klasa jest używana tylko przez 2 lub 3 inne, im więcej kodu w nagłówku, tym więcej pracy do zrobienia.

Jeśli funkcja członka definicje znajdują się w jednostce tłumaczeniowej (.plik cpp) własnych, następnie są kompilowane raz, a tylko deklaracje funkcji są kompilowane wielokrotnie.

Prawdą jest, że funkcje Członkowskie zdefiniowane (nie tylko zadeklarowane) w definicji klasy są niejawnie inline. Ale inline nie oznacza tego, co ludzie mogą rozsądnie przypuszczać, że to znaczy. inline mówi, że jest legalne, aby wiele definicji funkcji pojawiało się w różnych jednostkach tłumaczeniowych, a później były ze sobą połączone. To jest konieczne, jeśli Klasa znajduje się w pliku nagłówkowym, którego będą używać Różne pliki źródłowe, więc język stara się być pomocny.

inline jest to również wskazówka dla kompilatora, że funkcja może być użytecznie inlined, ale pomimo nazwy, że jest to opcjonalne. Im bardziej zaawansowany jest Twój kompilator, tym lepiej jest w stanie podejmować własne decyzje dotyczące inliningu i tym mniej potrzebuje podpowiedzi. Ważniejsze od rzeczywistego znacznika inline jest to, czy funkcja jest dostępna dla kompilatora w wszystkie. Jeśli funkcja jest zdefiniowana w innej jednostce tłumaczeniowej, to nie jest dostępna, gdy wywołanie do niej jest kompilowane, a więc jeśli cokolwiek ma być wbudowane w wywołanie, to musi to być linker, a nie kompilator.

Być może będziesz w stanie lepiej zobaczyć różnice, rozważając trzeci możliwy sposób zrobienia tego:

// File class.h
class MyClass
{
    private:
        //attributes
    public:
       void method1(...);
       void method2(...);
       ...
};

inline void MyClass::method1(...)
{
     //implementation
}

inline void MyClass::method2(...)
{
     //implementation
}

Teraz, gdy ukryta linia inline jest wyłączona, pozostają pewne różnice między tym podejściem "all header", a " header plus source" podejdźcie. Podział kodu między jednostki tłumaczeniowe ma konsekwencje dla tego, co dzieje się w trakcie jego budowy.

 35
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 17:07:09

Tak, kompilator spróbuje wprowadzić metodę zadeklarowaną bezpośrednio w pliku nagłówkowym, np.:

class A
{
 public:
   void method()
   {
   }
};

Myślę o następujących udogodnieniach w rozdzielaniu implementacji w plikach nagłówkowych:

  1. nie będziesz miał nadmiarowego kodu, ponieważ ten sam kod jest dołączany do wielu jednostek tłumaczeniowych
  2. twój czas kompilacji zmniejszy się drastycznie. Pamiętaj, że dla każdego modyfikacja w pliku nagłówkowym kompilator musi zbudować wszystkie inne pliki, które bezpośrednio lub pośrednio załącz to. Myślę, że będzie bardzo frustrujące dla każdego, aby zbudować cały binarny ponownie tylko dla dodania miejsce w pliku nagłówkowym.
 7
Author: Naveen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 16:45:45

Jakakolwiek zmiana nagłówka zawierającego implementację wymusi przekompilowanie i ponowne połączenie wszystkich klas zawierających ten nagłówek.

Ponieważ nagłówki zmieniają się rzadziej niż implementacje, umieszczając implementację w osobnym pliku, można zaoszczędzić znaczny czas kompilacji.

Jak już zauważyły inne odpowiedzi, tak, zdefiniowanie metody w bloku class pliku spowoduje, że kompilator będzie w linii.

 7
Author: Ben S,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 16:57:28

Tak, definiowanie metod wewnątrz definicji klasy jest równoważne deklarowaniu ich inline. Nie ma innej różnicy. Nie ma żadnej korzyści w definiowaniu wszystkiego w pliku nagłówkowym.

Coś takiego jest zwykle widoczne w C++ z klasami szablonów, ponieważ definicje członków szablonów muszą być również zawarte w pliku nagłówkowym (ze względu na fakt, że większość kompilatorów nie obsługuje export). Ale ze zwykłymi klasami nie-szablonowymi nie ma sensu tego robić, chyba że naprawdę chcesz zadeklarować Twoje metody jako inline.

 2
Author: AnT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 16:41:56

Dla mnie, główna różnica polega na tym, że plik nagłówkowy jest jak "interfejs" dla klasy, mówiąc klientom tej klasy, jakie są jej publiczne metody (operacje, które obsługuje), bez obawy klientów o konkretną implementację tych. W sensie jest to sposób na enkapsulację swoich klientów przed zmianami implementacyjnymi, ponieważ zmienia się tylko plik cpp, a co za tym idzie czas kompilacji jest znacznie krótszy.

 1
Author: lalitm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 16:03:43

Kiedyś stworzyłem moduł chroniący przed różnicami w różnych dystrybucjach CORBA i oczekiwano, że będzie on działał jednolicie na różnych kombinacjach OS / kompilator / CORBA lib. Zaimplementowanie go w pliku nagłówkowym ułatwiło dodanie go do projektu za pomocą prostego include. Ta sama technika gwarantowała, że kod został przekompilowany w tym samym czasie, gdy kod wywołujący go wymagał rekompilacji, np. gdy był kompilowany z inną biblioteką lub na innym systemie operacyjnym.

Więc chodzi mi o to, że jeśli masz raczej małą bibliotekę, która ma być wielokrotnego użytku i rekompilowalna w różnych projektach, co czyni ją nagłówkiem oferuje korzyści w integracji z innymi projektami, w przeciwieństwie do dodawania dodatkowych plików do głównego projektu lub rekompilacji zewnętrznego pliku lib / obj.

 0
Author: jszpilewski,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-03 15:36:34