Déclaration de fonction à l'intérieur ou à l'extérieur de la classe


90

Je suis un développeur JAVA qui essaie d'apprendre le C ++, mais je ne sais pas vraiment quelles sont les meilleures pratiques pour les déclarations de fonctions standard.

Dans la classe:

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

Ou à l'extérieur:

class Clazz
{
public:
    void Fun1();
}

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

J'ai le sentiment que le second peut être moins lisible ...


1
Il y a en fait 3 options ici. Votre deuxième exemple pourrait avoir la définition de fonction dans le fichier d'en-tête (mais toujours pas en ligne) ou dans un .cppfichier séparé .
Cody Gray

Cette question pourrait vous aider à comprendre.
Björn Pollex

3
Juste une note: la déclaration est toujours à l'intérieur de la classe, mais la définition est à l'intérieur ou à l'extérieur. Le titre et le corps de la question doivent être soumis à s / déclaration / définition / Ne me croyez pas? stackoverflow.com/q/1410563/1143274
Evgeni Sergeev

1
Les définitions de fonction à l'intérieur de la classe doivent être évitées. Ils sont réputés implicitement inline.
John Strood

@JohnStrood alors? inlineassouplit uniquement la règle d'une définition, ce qui est nécessaire si une autre unité de traduction utiliseClazz
Caleth

Réponses:


57

C ++ est orienté objet, en ce sens qu'il prend en charge le paradigme orienté objet pour le développement logiciel.

Cependant, à la différence de Java, C ++ ne vous oblige pas à regrouper les définitions de fonctions dans des classes: la méthode standard C ++ pour déclarer une fonction est de simplement déclarer une fonction, sans aucune classe.

Si à la place vous parlez de déclaration / définition de méthode, la méthode standard consiste à placer uniquement la déclaration dans un fichier include (normalement nommé .hou .hpp) et la définition dans un fichier d'implémentation séparé (normalement nommé .cppou .cxx). Je conviens que c'est en effet quelque peu ennuyeux et nécessite une certaine duplication, mais c'est ainsi que le langage a été conçu.

Pour des expériences rapides et des projets sur un seul fichier, tout fonctionnerait ... mais pour des projets plus importants, cette séparation est pratiquement nécessaire.

Remarque: même si vous connaissez Java, C ++ est un langage complètement différent ... et c'est un langage qui ne peut pas être appris en expérimentant. La raison en est que c'est un langage assez complexe avec beaucoup d'asymétries et des choix apparemment illogiques, et surtout, lorsque vous faites une erreur, il n'y a pas d '"anges d'erreur d'exécution" pour vous sauver comme en Java ... mais il y en a à la place " démons de comportement indéfinis ".

La seule façon raisonnable d'apprendre le C ++ est de lire ... peu importe à quel point vous êtes intelligent, vous ne pouvez pas deviner ce que le comité a décidé (être intelligent est parfois même un problème parce que la bonne réponse est illogique et est une conséquence patrimoine.)

Choisissez simplement un ou deux bons livres et lisez-les d'un bout à l'autre.


7
Si quelqu'un vient de Java et demande de l'aide sur C ++, que lui dit-il si vous dites «le langage que vous connaissez est obsédé par quelque chose»? Il n'a pas de comparaison avec d'autres langues, donc cela ne lui dit pratiquement rien. Mieux que d'utiliser un mot fortement connoté émotionnellement comme obsédé, qui ne dit pas grand-chose au PO, vous pourriez envisager de laisser cette partie de côté. De plus, quel est le contexte de «utiliser une classe pour tout»? En Java, vous n'utilisez pas de classe pour une méthode. Vous n'utilisez pas de classe pour une variable. Vous n'utilisez pas de classe pour un fichier..Alors qu'est-ce que "tout" ici? Ranting?
Daniel S.20

3
@DanielS: Supprimé cette partie parce que vous avez apparemment offensé (je ne sais pas pourquoi). Bien sûr, je ne discute pas à propos de Java parce que je n'utilise pas du tout Java, j'ai simplement pensé à l'époque que la POO en tant que programmation obsédée par les objets était une blague amusante, alors qu'apparemment ce n'est pas le cas. J'ai été un programmeur certifié Java 1.1 mais j'ai décidé à l'époque que, à moins d'être forcé pour une raison quelconque, je n'utiliserai pas ce "langage de programmation" et jusqu'à présent, j'ai réussi à l'éviter.
6502

Merci, je pense que ça se lit beaucoup mieux maintenant. Désolé si j'ai l'air offensé. J'essaierai d'être plus positif la prochaine fois.
Daniel S.20

13
Ne répond pas à la question
Petr Peller

1
@PetrPeller: quelle est la partie du troisième paragraphe qui n'est pas claire pour vous?
6502

26

Le premier définit votre fonction membre comme une fonction en ligne , tandis que le second ne le fait pas. La définition de la fonction dans ce cas réside dans l'en-tête lui-même.

La deuxième implémentation placerait la définition de la fonction dans le fichier cpp.

Les deux sont sémantiquement différents et ce n'est pas seulement une question de style.


2
cplusplus.com/doc/tutorial/classes donne la même réponse: "La seule différence entre définir une fonction membre de classe complètement dans sa classe ou inclure uniquement le prototype et plus tard sa définition, est que dans le premier cas la fonction sera automatiquement considérée comme une fonction membre en ligne par le compilateur, alors que dans la seconde, ce sera une fonction membre de classe normale (non en ligne), qui en fait ne suppose aucune différence de comportement. "
Buttons840

18

La définition des fonctions est meilleure en dehors de la classe. De cette façon, votre code peut rester en sécurité si nécessaire. Le fichier d'en-tête doit simplement donner des déclarations.

Supposons que quelqu'un veuille utiliser votre code, vous pouvez simplement lui donner le fichier .h et le fichier .obj (obtenu après compilation) de votre classe. Il n'a pas besoin du fichier .cpp pour utiliser votre code.

De cette façon, votre implémentation n'est visible par personne d'autre.


10

La méthode «À l'intérieur de la classe» (I) fait la même chose que la méthode «À l'extérieur de la classe» (O).

Cependant, (I) peut être utilisé lorsqu'une classe n'est utilisée que dans un seul fichier (dans un fichier .cpp). (O) est utilisé lorsqu'il se trouve dans un fichier d'en-tête. Les fichiers cpp sont toujours compilés. Les fichiers d'en-tête sont compilés lorsque vous utilisez #include "header.h".

Si vous utilisez (I) dans un fichier d'en-tête, la fonction (Fun1) sera déclarée à chaque fois que vous incluez #include "header.h". Cela peut conduire à déclarer la même fonction plusieurs fois. Ceci est plus difficile à compiler et peut même entraîner des erreurs.

Exemple d'utilisation correcte:

Fichier1: "Clazz.h"

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

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

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

3

Les fonctions membres peuvent être définies dans la définition de classe ou séparément à l'aide de l'opérateur de résolution de portée, ::. La définition d'une fonction membre dans la définition de classe déclare la fonction en ligne, même si vous n'utilisez pas le spécificateur en ligne. Donc, soit vous pouvez définir la fonction Volume () comme ci-dessous:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

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

Si vous le souhaitez, vous pouvez définir la même fonction en dehors de la classe en utilisant l'opérateur de résolution de portée, :: comme suit

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

Ici, le seul point important est que vous devrez utiliser le nom de la classe juste avant :: operator. Une fonction membre sera appelée à l'aide d'un opérateur point (.) Sur un objet où elle manipulera les données liées à cet objet uniquement comme suit:

Box myBox;           

myBox.getVolume();  

(à partir de: http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm ), les deux méthodes sont légales.

Je ne suis pas un expert, mais je pense que si vous ne mettez qu'une seule définition de classe dans un fichier, cela n'a pas vraiment d'importance.

mais si vous appliquez quelque chose comme la classe interne, ou si vous avez plusieurs définitions de classe, la seconde serait difficile à lire et à maintenir.


1
Pouvez-vous intégrer le contenu pertinent de ce lien dans le corps de votre message, et ainsi vous protéger contre les liens morts pour l'avenir? Merci
JustinJDavies

2

Le premier doit être placé dans le fichier d'en-tête (où réside la déclaration de la classe). Le second peut être n'importe où, soit l'en-tête ou, généralement, un fichier source. En pratique, vous pouvez mettre de petites fonctions dans la déclaration de classe (qui les déclare implicitement en ligne, bien que ce soit le compilateur qui décide en fin de compte si elles seront insérées ou non). Cependant, la plupart des fonctions ont une déclaration dans l'en-tête et l'implémentation dans un fichier cpp, comme dans votre deuxième exemple. Et non, je ne vois aucune raison pour laquelle cela serait moins lisible. Sans oublier que vous pouvez en fait diviser l'implémentation d'un type sur plusieurs fichiers cpp.


1

Une fonction définie dans une classe est par défaut traitée comme une fonction en ligne. Une raison simple pour laquelle vous devez définir votre fonction à l'extérieur:

Un constructeur de la classe vérifie les fonctions virtuelles et initialise un pointeur virtuel pour pointer vers le VTABLE approprié ou la table de méthode virtuelle , appelle le constructeur de la classe de base et initialise les variables de la classe actuelle, de sorte qu'il effectue un certain travail.

Les fonctions en ligne sont utilisées lorsque les fonctions ne sont pas si compliquées et évitent la surcharge de l'appel de fonction. (La surcharge comprend un saut et une branche au niveau matériel.) Et comme décrit ci-dessus, le constructeur n'est pas aussi simple à considérer que celui en ligne.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.