Dichiarazione di funzione all'interno o all'esterno della classe


91

Sono uno sviluppatore JAVA che sta cercando di imparare il C ++, ma non so davvero quale sia la migliore pratica per le dichiarazioni di funzioni standard.

In classe:

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

O fuori:

class Clazz
{
public:
    void Fun1();
}

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

Ho la sensazione che il secondo possa essere meno leggibile ...


1
In realtà ci sono 3 opzioni qui. Il secondo esempio potrebbe avere la definizione della funzione nel file di intestazione (ma non ancora inline) o in un .cppfile separato .
Cody Grey

Questa domanda potrebbe aiutarti a capire.
Björn Pollex

3
Solo una nota: la dichiarazione è sempre all'interno della classe, ma la definizione è all'interno o all'esterno. Il titolo e il corpo della domanda devono essere soggetti a / dichiarazione / definizione / Non mi credete? stackoverflow.com/q/1410563/1143274
Evgeni Sergeev

1
Le definizioni di funzione all'interno della classe devono essere evitate. Sono considerati implicitamente inline.
John Strood

@JohnStrood così? inlineallenta solo la regola di una definizione, che è necessaria se un'altra unità di traduzione utilizzaClazz
Caleth

Risposte:


57

C ++ è orientato agli oggetti, nel senso che supporta il paradigma orientato agli oggetti per lo sviluppo del software.

Tuttavia, a differenza di Java, il C ++ non ti obbliga a raggruppare le definizioni di funzione in classi: il modo C ++ standard per dichiarare una funzione è semplicemente dichiarare una funzione, senza alcuna classe.

Se invece stai parlando di dichiarazione / definizione di metodo, il modo standard è mettere solo la dichiarazione in un file di inclusione (normalmente chiamato .hor .hpp) e la definizione in un file di implementazione separato (normalmente chiamato .cppor .cxx). Sono d'accordo che questo è davvero un po 'fastidioso e richiede alcuni duplicati, ma è così che è stato progettato il linguaggio.

Per esperimenti rapidi e progetti su file singolo, qualsiasi cosa funzionerebbe ... ma per progetti più grandi questa separazione è qualcosa che è praticamente richiesta.

Nota: anche se conosci Java, C ++ è un linguaggio completamente diverso ... ed è un linguaggio che non può essere appreso sperimentando. Il motivo è che è un linguaggio piuttosto complesso con molte asimmetrie e scelte apparentemente illogiche e, cosa più importante, quando commetti un errore non ci sono "angeli dell'errore di runtime" a salvarti come in Java ... ma invece ci sono " demoni di comportamento indefinito ".

L'unico modo ragionevole per imparare il C ++ è leggere ... non importa quanto tu sia intelligente, non c'è modo di indovinare cosa ha deciso il comitato (in realtà essere intelligente a volte è persino un problema perché la risposta corretta è illogica e una conseguenza eredità.)

Scegli uno o due buoni libri e leggili dall'inizio alla fine.


7
Se qualcuno arriva da Java e chiede aiuto su C ++, cosa gli dice se dici "il linguaggio che conosci è ossessionato da qualcosa"? Non ha paragoni con altre lingue, quindi questo non gli dice praticamente nulla. Meglio che usare una parola fortemente connotata emotivamente come ossessionato, che non dice molto all'OP, potresti considerare di lasciare questa parte fuori. Inoltre, qual è il contesto di "usa una classe per ogni cosa"? In Java, non usi una classe per un metodo. Non usi una classe per una variabile. Non usi una classe per un file .. Allora cos'è "tutto" qui? Ranting?
Daniel S.

3
@ DanielS: Rimossa quella parte perché apparentemente ti ha offeso (non ho idea del perché). Di sicuro non mi sto lamentando di Java perché in realtà non lo uso affatto, all'epoca pensavo semplicemente che OOP come Object Obsessed Programming fosse uno scherzo divertente, mentre a quanto pare non lo è. Sono stato un programmatore certificato Java 1.1 ma ho deciso allora che, a meno che non fosse costretto per qualche motivo, non utilizzerò quel "linguaggio di programmazione" e finora sono riuscito ad evitarlo.
6502

Grazie, penso che si legga molto meglio ora. Scusa se sembro offeso. La prossima volta cercherò di essere più positivo.
Daniel S.

15
Non risponde alla domanda
Petr Peller

1
@PetrPeller: qual è la parte del terzo paragrafo che non ti è chiara?
6502

27

Il primo definisce la tua funzione membro come una funzione inline , mentre il secondo no. La definizione della funzione in questo caso risiede nell'intestazione stessa.

La seconda implementazione collocherebbe la definizione della funzione nel file cpp.

Entrambi sono semanticamente diversi e non è solo una questione di stile.


2
cplusplus.com/doc/tutorial/classes dà la stessa risposta: "L'unica differenza tra definire una funzione membro di una classe completamente all'interno della sua classe o includere solo il prototipo e successivamente la sua definizione, è che nel primo caso la funzione sarà automaticamente considerata una funzione membro inline dal compilatore, mentre nel secondo sarà una funzione membro di classe normale (non inline), che di fatto non suppone differenze di comportamento. "
Pulsanti840

18

La definizione della funzione è migliore al di fuori della classe. In questo modo il tuo codice può rimanere al sicuro se necessario. Il file di intestazione dovrebbe solo fornire dichiarazioni.

Supponiamo che qualcuno voglia usare il tuo codice, puoi semplicemente dargli il file .h e il file .obj (ottenuto dopo la compilazione) della tua classe. Non ha bisogno del file .cpp per usare il tuo codice.

In questo modo la tua implementazione non sarà visibile a nessun altro.


10

Il metodo "Inside the class" (I) fa la stessa cosa del metodo "outside the class" (O).

Tuttavia, (I) può essere utilizzato quando una classe viene utilizzata solo in un file (all'interno di un file .cpp). (O) viene utilizzato quando si trova in un file di intestazione. I file cpp vengono sempre compilati. I file di intestazione vengono compilati quando si utilizza #include "header.h".

Se usi (I) in un file di intestazione, la funzione (Fun1) verrà dichiarata ogni volta che includi #include "header.h". Ciò può portare a dichiarare la stessa funzione più volte. Questo è più difficile da compilare e può persino portare a errori.

Esempio per un corretto utilizzo:

File1: "Clazz.h"

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

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

File2: "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. 

Intendi Clazz MyClazze Clazz MyClazz2?
Chupo_cro

4

Le funzioni membro possono essere definite all'interno della definizione della classe o separatamente utilizzando l'operatore di risoluzione dell'ambito, ::. La definizione di una funzione membro all'interno della definizione di classe dichiara la funzione inline, anche se non si utilizza l'identificatore inline. Quindi puoi definire la funzione Volume () come di seguito:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

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

Se lo desideri, puoi definire la stessa funzione al di fuori della classe utilizzando l'operatore di risoluzione dell'ambito, :: come segue

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

Qui, l'unico punto importante è che dovresti usare il nome della classe appena prima dell'operatore ::. Una funzione membro verrà chiamata utilizzando un operatore punto (.) Su un oggetto in cui manipolerà i dati relativi a quell'oggetto solo come segue:

Box myBox;           

myBox.getVolume();  

(da: http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm ), entrambi i modi sono legali.

Non sono un esperto, ma penso che se metti solo una definizione di classe in un file, allora non ha molta importanza.

ma se applichi qualcosa come la classe interna o hai più definizioni di classe, la seconda sarebbe difficile da leggere e mantenere.


1
Puoi portare il contenuto pertinente da quel link nel corpo del tuo post, e quindi a prova di futuro contro i link morti? Grazie
JustinJDavies

2

Il primo deve essere inserito nel file di intestazione (dove risiede la dichiarazione della classe). Il secondo può essere ovunque, nell'intestazione o, di solito, in un file sorgente. In pratica puoi mettere piccole funzioni nella dichiarazione della classe (che le dichiara implicitamente inline, sebbene sia il compilatore che alla fine decide se saranno inline o meno). Tuttavia, la maggior parte delle funzioni ha una dichiarazione nell'intestazione e l'implementazione in un file cpp, come nel secondo esempio. E no, non vedo alcun motivo per cui questo sarebbe meno leggibile. Per non parlare del fatto che potresti dividere l'implementazione per un tipo su diversi file cpp.


1

Una funzione definita all'interno di una classe viene trattata per impostazione predefinita come una funzione inline. Un semplice motivo per cui dovresti definire la tua funzione all'esterno:

Un costruttore della classe controlla le funzioni virtuali e inizializza un puntatore virtuale per puntare al VTABLE appropriato o alla tabella del metodo virtuale , chiama il costruttore della classe di base e inizializza le variabili della classe corrente, in modo che effettivamente funzioni.

Le funzioni inline vengono utilizzate quando le funzioni non sono così complicate ed evita l'overhead della chiamata di funzione. (L'overhead include un salto e un ramo a livello di hardware.) E come descritto sopra, il costruttore non è così semplice da essere considerato come inline.


"inline" non ha praticamente nulla a che fare con inlining. Il fatto che le funzioni membro definite in linea siano implicitamente dichiarate in linea è lì per evitare violazioni ODR.
Big Temp

0

Funzioni inline (funzioni quando le dichiari nella classe) ogni volta che le chiami vengono incollate nel codice della memoria principale. Mentre quando dichiari la funzione fuori dalla classe, quando chiami la funzione proviene dalla stessa memoria. Ecco perché è molto meglio.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.