Deklaracja funkcji wewnątrz lub na zewnątrz klasy

91

Jestem programistą JAVA, który próbuje nauczyć się C ++, ale tak naprawdę nie wiem, jaka jest najlepsza praktyka w przypadku standardowych deklaracji funkcji.

W klasie:

class Clazz
{
 public:
    void Fun1()
    {
        //do something
    }
}

Lub na zewnątrz:

class Clazz
{
public:
    void Fun1();
}

Clazz::Fun1(){
    // Do something
}

Mam wrażenie, że ta druga może być mniej czytelna ...

JohnJohnGa
źródło
1
W rzeczywistości są tutaj 3 opcje. Twój drugi przykład może mieć definicję funkcji w pliku nagłówkowym (ale nadal nie jest wstawiona) lub w oddzielnym .cpppliku.
Cody Gray
To pytanie może pomóc ci to zrozumieć.
Björn Pollex
3
Uwaga: deklaracja zawsze znajduje się wewnątrz klasy, ale definicja jest wewnątrz lub na zewnątrz. Tytuł i treść pytania powinny podlegać s / deklaracji / definicji / Nie wierzysz mi? stackoverflow.com/q/1410563/1143274
Evgeni Sergeev
1
Należy unikać definicji funkcji wewnątrz klasy. Są uznawane w sposób dorozumiany inline.
John Strood
@JohnStrood so? inlineClazz
łagodzi

Odpowiedzi:

57

C ++ jest zorientowany obiektowo w tym sensie, że wspiera paradygmat obiektowy w tworzeniu oprogramowania.

Jednak inaczej niż w Javie, C ++ nie wymusza grupowania definicji funkcji w klasach: standardowym sposobem C ++ deklarowania funkcji jest po prostu zadeklarowanie funkcji bez żadnej klasy.

Jeśli zamiast tego mówisz o deklaracji / definicji metody, to standardowym sposobem jest umieszczenie tylko deklaracji w pliku dołączanym (zwykle o nazwie .hlub .hpp), a definicja w oddzielnym pliku implementacji (zwykle o nazwie .cpplub .cxx). Zgadzam się, że jest to rzeczywiście trochę denerwujące i wymaga pewnych powtórzeń, ale tak zaprojektowano język.

W przypadku szybkich eksperymentów i projektów jednoplikowych wszystko by działało ... ale w przypadku większych projektów ta separacja jest praktycznie wymagana.

Uwaga: nawet jeśli znasz Javę, C ++ to zupełnie inny język ... i jest to język, którego nie można się nauczyć eksperymentując. Powodem jest to, że jest to dość złożony język z wieloma asymetriami i pozornie nielogicznymi wyborami, a co najważniejsze, kiedy popełnisz błąd, nie ma żadnych „aniołów błędów uruchomieniowych”, które uratują Cię tak, jak w Javie ... ale zamiast tego są ” niezdefiniowane demony zachowania ”.

Jedynym rozsądnym sposobem nauki C ++ jest czytanie ... bez względu na to, jak mądry jesteś, w żaden sposób nie możesz zgadnąć, co zdecydowała komisja (bycie mądrym czasami jest nawet problemem, ponieważ prawidłowa odpowiedź jest nielogiczna i wynika z historii dziedzictwo.)

Po prostu wybierz dobrą książkę lub dwie i przeczytaj je od deski do deski.

6502
źródło
7
Jeśli ktoś pochodzi z Javy i prosi o pomoc w C ++, to co mu powie, jeśli powiesz „język, który znasz, ma obsesję na punkcie czegoś”? Nie ma porównania do innych języków, więc prawie nic mu to nie mówi. Lepiej niż używać silnie emocjonalnie kojarzonego słowa, takiego jak obsesja, które nie mówi zbyt wiele OP, możesz rozważyć po prostu pominięcie tej części. Co więcej, jaki jest kontekst „używaj klasy do wszystkiego”? W Javie nie używa się klasy dla metody. Nie używasz klasy dla zmiennej. Nie używasz klasy dla pliku… Więc co tu jest „wszystko”? Ranting?
Daniel S.
3
@DanielS: Usunąłem tę część, ponieważ najwyraźniej cię uraził (nie mam pojęcia dlaczego). Na pewno nie narzekam na Javę, ponieważ w ogóle nie używam Javy, po prostu myślałem wtedy, że OOP jako programowanie z obsesją obiektów to zabawny żart, podczas gdy najwyraźniej tak nie jest. Byłem programistą z certyfikatem Java 1.1, ale wtedy zdecydowałem, że jeśli nie będę zmuszony z jakiegoś powodu, nie będę używać tego „języka programowania” i do tej pory udało mi się go uniknąć.
6502
Dzięki, myślę, że teraz brzmi znacznie lepiej. Przepraszam, jeśli brzmię urażony. Następnym razem postaram się być bardziej pozytywnym.
Daniel S.,
15
Nie odpowiada na pytanie
Petr Peller
1
@PetrPeller: jaka część trzeciego akapitu nie jest dla Ciebie jasna?
6502
27

Pierwsza definiuje twoją funkcję członkowską jako funkcję wbudowaną , a druga nie. Definicja funkcji w tym przypadku znajduje się w samym nagłówku.

Druga implementacja umieściłaby definicję funkcji w pliku cpp.

Oba różnią się semantycznie i nie jest to tylko kwestia stylu.

Alok Save
źródło
2
cplusplus.com/doc/tutorial/classes podaje tę samą odpowiedź: „Jedyną różnicą między zdefiniowaniem funkcji składowej klasy w całości w jej klasie lub dołączeniem tylko prototypu i późniejszą definicją jest to, że w pierwszym przypadku funkcja zostanie automatycznie kompilator uważany za wbudowaną funkcję składową, podczas gdy w drugiej będzie normalną (nie wbudowaną) funkcją składową klasy, co w rzeczywistości nie zakłada żadnej różnicy w zachowaniu. "
Przyciski 840,
18

Definicja funkcji jest lepsza poza klasą. W ten sposób Twój kod może pozostać bezpieczny w razie potrzeby. Plik nagłówkowy powinien zawierać tylko deklaracje.

Załóżmy, że ktoś chce użyć Twojego kodu, możesz po prostu przekazać mu plik .h i plik .obj (uzyskany po kompilacji) swojej klasy. Nie potrzebuje pliku .cpp, aby użyć Twojego kodu.

Dzięki temu Twoja implementacja nie będzie widoczna dla nikogo innego.

Ajit Vaze
źródło
10

Metoda „wewnątrz klasy” (I) działa tak samo, jak metoda „poza klasą” (O).

Jednak (I) może być używane, gdy klasa jest używana tylko w jednym pliku (wewnątrz pliku .cpp). (O) jest używane, gdy znajduje się w pliku nagłówkowym. Pliki cpp są zawsze kompilowane. Pliki nagłówkowe są kompilowane, gdy używasz #include "header.h".

Jeśli użyjesz (I) w pliku nagłówkowym, funkcja (Fun1) zostanie zadeklarowana za każdym razem, gdy dodasz #include "header.h". Może to prowadzić do wielokrotnego deklarowania tej samej funkcji. Jest to trudniejsze do skompilowania, a nawet może prowadzić do błędów.

Przykład prawidłowego użycia:

Plik1: „Clazz.h”

//This file sets up the class with a prototype body. 

class Clazz
{
public:
    void Fun1();//This is a Fun1 Prototype. 
};

Plik2: „Clazz.cpp”

#include "Clazz.h" 
//this file gives Fun1() (prototyped in the header) a body once.

void Clazz::Fun1()
{
    //Do stuff...
}

File3: „UseClazz.cpp”

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz;
MyClazz.Fun1();//This does Fun1, as prototyped in the header.

File4: „AlsoUseClazz.cpp”

#include "Clazz.h" 
//This file uses Fun1() but does not care where Fun1 was given a body. 

class MyClazz2;
MyClazz2.Fun1();//This does Fun1, as prototyped in the header. 

File5: „DoNotUseClazzHeader.cpp”

//here we do not include Clazz.h. So this is another scope. 
class Clazz
{
public:
    void Fun1()
    {
         //Do something else...
    }
};

class MyClazz; //this is a totally different thing. 
MyClazz.Fun1(); //this does something else. 
Maarten t Hart
źródło
Masz na myśli Clazz MyClazzi Clazz MyClazz2?
Chupo_cro
4

Funkcje składowe można definiować w ramach definicji klasy lub oddzielnie za pomocą operatora rozpoznawania zakresu, ::. Definiowanie funkcji składowej w definicji klasy deklaruje funkcję w tekście, nawet jeśli nie używasz specyfikatora wbudowanego. Możesz więc zdefiniować funkcję Volume () jak poniżej:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

     double getVolume(void)
     {
        return length * breadth * height;
     }
};

Jeśli chcesz, możesz zdefiniować tę samą funkcję poza klasą za pomocą operatora rozpoznawania zasięgu, :: w następujący sposób

double Box::getVolume(void)
{
   return length * breadth * height;
}

Tutaj jedyną ważną kwestią jest to, że musisz użyć nazwy klasy tuż przed operatorem ::. Funkcja członkowska zostanie wywołana za pomocą operatora kropki (.) Na obiekcie, w którym będzie manipulować danymi związanymi z tym obiektem tylko w następujący sposób:

Box myBox;           

myBox.getVolume();  

(z: http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm ), oba sposoby są legalne.

Nie jestem ekspertem, ale myślę, że jeśli umieścisz tylko jedną definicję klasy w jednym pliku, to nie ma to większego znaczenia.

ale jeśli zastosujesz coś takiego jak klasa wewnętrzna lub masz wiele definicji klas, druga byłaby trudna do odczytania i utrzymania.

user116541
źródło
1
Czy możesz umieścić odpowiednią treść z tego linku w treści swojego posta, a tym samym zabezpieczyć się przed martwymi linkami w przyszłości? Dzięki
JustinJDavies
2

Pierwszą należy umieścić w pliku nagłówkowym (w którym znajduje się deklaracja klasy). Drugi może znajdować się w dowolnym miejscu, albo w nagłówku, albo zazwyczaj w pliku źródłowym. W praktyce możesz umieścić małe funkcje w deklaracji klasy (która deklaruje je niejawnie w tekście, chociaż to kompilator ostatecznie decyduje, czy będą one wstawiane, czy nie). Jednak większość funkcji ma deklarację w nagłówku i implementację w pliku cpp, tak jak w drugim przykładzie. I nie, nie widzę powodu, dla którego byłoby to mniej czytelne. Nie wspominając o tym, że możesz faktycznie podzielić implementację dla typu na kilka plików cpp.

Marius Bancila
źródło
1

Funkcja zdefiniowana wewnątrz klasy jest domyślnie traktowana jako funkcja wbudowana. Prosty powód, dla którego powinieneś zdefiniować swoją funkcję na zewnątrz:

Konstruktor klasy sprawdza funkcje wirtualne i inicjuje wirtualny wskaźnik wskazujący na właściwą tabelę VTABLE lub wirtualnej metody , wywołuje konstruktor klasy bazowej i inicjuje zmienne bieżącej klasy, więc faktycznie wykonuje jakąś pracę.

Funkcje wbudowane są używane, gdy funkcje nie są tak skomplikowane i pozwalają uniknąć narzutu wywołania funkcji. (Narzut obejmuje skok i rozgałęzienie na poziomie sprzętowym). I jak opisano powyżej, konstruktor nie jest tak łatwy do rozważenia jak wbudowany.

R Mehta
źródło
„inline” nie ma praktycznie nic wspólnego z inliningiem. Fakt, że funkcje składowe zdefiniowane w linii są niejawnie deklarowane jako wbudowane, ma na celu uniknięcie naruszeń ODR.
Big Temp
0

Funkcje wbudowane (funkcje, gdy deklarujesz je w klasie) za każdym razem, gdy je wywołasz, są wklejane do twojego głównego kodu pamięci. Podczas gdy deklarujesz funkcję poza klasą, wywołanie funkcji pochodzi z tej samej pamięci. Dlatego jest o wiele lepiej.

elegancki
źródło