Funktionsdeklaration innerhalb oder außerhalb der Klasse

89

Ich bin ein JAVA-Entwickler, der versucht, C ++ zu lernen, aber ich weiß nicht wirklich, was die beste Vorgehensweise für Standardfunktionsdeklarationen ist.

In der Klasse:

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

Oder draußen:

class Clazz
{
public:
    void Fun1();
}

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

Ich habe das Gefühl, dass der zweite weniger lesbar sein kann ...

JohnJohnGa
quelle
1
Hier gibt es tatsächlich 3 Optionen. Ihr zweites Beispiel könnte die Funktionsdefinition in der Header-Datei (aber immer noch nicht inline) oder in einer separaten .cppDatei haben.
Cody Gray
Diese Frage könnte Ihnen beim Verständnis helfen.
Björn Pollex
3
Nur eine Anmerkung: Die Deklaration befindet sich immer innerhalb der Klasse, die Definition erfolgt jedoch entweder innerhalb oder außerhalb. Der Titel und der Text der Frage sollten s / Erklärung / Definition / Glauben Sie mir nicht unterliegen? stackoverflow.com/q/1410563/1143274
Evgeni Sergeev
1
Funktionsdefinitionen innerhalb der Klasse müssen vermieden werden. Sie gelten implizit inline.
John Strood
@ JohnStrood so? inlinelockert nur die eine Definitionsregel, die notwendig ist, wenn eine andere Übersetzungseinheit verwendetClazz
Caleth

Antworten:

55

C ++ ist objektorientiert in dem Sinne, dass es das objektorientierte Paradigma für die Softwareentwicklung unterstützt.

Anders als in Java zwingt C ++ Sie jedoch nicht dazu, Funktionsdefinitionen in Klassen zu gruppieren: Die Standardmethode zum Deklarieren einer Funktion in C ++ besteht darin, nur eine Funktion ohne Klasse zu deklarieren.

Wenn Sie stattdessen von Methodendeklaration / -definition sprechen, besteht die Standardmethode darin, nur die Deklaration in eine Include-Datei (normalerweise benannt .hoder .hpp) und die Definition in eine separate Implementierungsdatei (normalerweise benannt .cppoder .cxx) zu legen . Ich bin damit einverstanden, dass dies in der Tat etwas ärgerlich ist und einige Überschneidungen erfordert, aber so wurde die Sprache entworfen.

Für schnelle Experimente und Einzeldateiprojekte würde alles funktionieren ... aber für größere Projekte ist diese Trennung praktisch erforderlich.

Hinweis: Auch wenn Sie Java kennen, ist C ++ eine völlig andere Sprache ... und diese Sprache kann nicht durch Experimentieren erlernt werden. Der Grund ist, dass es eine ziemlich komplexe Sprache mit vielen Asymmetrien und scheinbar unlogischen Entscheidungen ist, und vor allem, wenn Sie einen Fehler machen, gibt es keine "Laufzeitfehler-Engel", die Sie wie in Java retten könnten ... aber es gibt stattdessen " undefinierte Verhaltensdämonen ".

Der einzig vernünftige Weg, um C ++ zu lernen, ist das Lesen ... egal wie klug Sie sind, Sie können nicht erraten, was das Komitee entschieden hat (tatsächlich ist es manchmal sogar ein Problem, klug zu sein, da die richtige Antwort unlogisch und eine Folge der Geschichte ist Erbe.)

Wählen Sie einfach ein oder zwei gute Bücher aus und lesen Sie sie vollständig durch.

6502
quelle
7
Wenn jemand aus Java kommt und um Hilfe in C ++ bittet, was sagt es ihm dann, wenn Sie sagen "Die Sprache, die Sie kennen, ist von etwas besessen"? Er hat keinen Vergleich zu anderen Sprachen, daher sagt ihm das so ziemlich nichts. Besser als ein stark emotional konnotiertes Wort wie "besessen" zu verwenden, das dem OP nicht viel sagt, könnten Sie in Betracht ziehen, diesen Teil einfach wegzulassen. Was ist darüber hinaus der Kontext von "Verwenden Sie eine Klasse für alles"? In Java verwenden Sie keine Klasse für eine Methode. Sie verwenden keine Klasse für eine Variable. Sie verwenden keine Klasse für eine Datei. Also, was ist "alles" hier? Schimpfen?
Daniel S.
3
@ DanielS: Dieser Teil wurde entfernt, weil er dich anscheinend beleidigt hat (keine Ahnung warum). Sicher, ich schimpfe nicht über Java, weil ich Java überhaupt nicht benutze. Ich dachte damals einfach, dass OOP als objektbesessene Programmierung ein lustiger Witz ist, obwohl es anscheinend nicht so ist. Ich war ein Java 1.1-zertifizierter Programmierer, habe aber damals entschieden, dass ich diese "Programmiersprache" nicht verwenden werde, wenn ich nicht aus irgendeinem Grund dazu gezwungen werde, und bisher ist es mir gelungen, sie zu vermeiden.
6502
Danke, ich denke es liest sich jetzt viel besser. Entschuldigung, wenn ich beleidigt klinge. Ich werde versuchen, das nächste Mal positiver zu sein.
Daniel S.
13
Beantwortet die Frage nicht
Petr Peller
1
@PetrPeller: Was ist der Teil des dritten Absatzes, der Ihnen nicht klar ist?
6502
25

Die erste definiert Ihre Mitgliedsfunktion als Inline-Funktion , die zweite nicht. Die Definition der Funktion befindet sich in diesem Fall im Header selbst.

Die zweite Implementierung würde die Definition der Funktion in die cpp-Datei einfügen.

Beide sind semantisch unterschiedlich und es ist nicht nur eine Frage des Stils.

Alok Speichern
quelle
2
cplusplus.com/doc/tutorial/classes gibt die gleiche Antwort: "Der einzige Unterschied zwischen der vollständigen Definition einer Klassenmitgliedsfunktion innerhalb ihrer Klasse oder der Einbeziehung nur des Prototyps und später seiner Definition besteht darin, dass die Funktion im ersten Fall automatisch ausgeführt wird wird vom Compiler als Inline-Member-Funktion betrachtet, während es sich in der zweiten um eine normale (nicht inline) Klassen-Member-Funktion handelt, die tatsächlich keinen Unterschied im Verhalten voraussetzt. "
Buttons840
18

Die Funktionsdefinition ist außerhalb der Klasse besser. Auf diese Weise kann Ihr Code bei Bedarf sicher bleiben. Die Header-Datei sollte nur Deklarationen enthalten.

Angenommen, jemand möchte Ihren Code verwenden, können Sie ihm einfach die .h-Datei und die .obj-Datei (nach dem Kompilieren erhalten) Ihrer Klasse geben. Er benötigt die CPP-Datei nicht, um Ihren Code zu verwenden.

Auf diese Weise ist Ihre Implementierung für niemanden sichtbar.

Ajit Vaze
quelle
10

Die Methode "Innerhalb der Klasse" (I) entspricht der Methode "Außerhalb der Klasse" (O).

(I) kann jedoch verwendet werden, wenn eine Klasse nur in einer Datei (innerhalb einer CPP-Datei) verwendet wird. (O) wird verwendet, wenn es sich in einer Header-Datei befindet. CPP-Dateien werden immer kompiliert. Header-Dateien werden kompiliert, wenn Sie #include "header.h" verwenden.

Wenn Sie (I) in einer Header-Datei verwenden, wird die Funktion (Fun1) jedes Mal deklariert, wenn Sie #include "header.h" einfügen. Dies kann dazu führen, dass dieselbe Funktion mehrmals deklariert wird. Dies ist schwieriger zu kompilieren und kann sogar zu Fehlern führen.

Beispiel für die korrekte Verwendung:

Datei1: "Clazz.h"

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

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

Datei2: "Clazz.cpp"

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

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

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

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

Datei5: "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
quelle
3

Elementfunktionen können innerhalb der Klassendefinition oder separat mit dem Bereichsauflösungsoperator :: definiert werden. Durch das Definieren einer Elementfunktion innerhalb der Klassendefinition wird die Funktion inline deklariert, auch wenn Sie den Inline-Bezeichner nicht verwenden. Sie können also entweder die Volume () - Funktion wie folgt definieren:

class Box
{
  public:

     double length;
     double breadth;    
     double height;     

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

Wenn Sie möchten, können Sie dieselbe Funktion außerhalb der Klasse mit dem Bereichsauflösungsoperator :: wie folgt definieren

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

Hier ist nur wichtig, dass Sie den Klassennamen kurz vor dem Operator :: verwenden müssen. Eine Elementfunktion wird mit einem Punktoperator (.) Für ein Objekt aufgerufen, bei dem Daten, die sich auf dieses Objekt beziehen, nur wie folgt bearbeitet werden:

Box myBox;           

myBox.getVolume();  

(von: http://www.tutorialspoint.com/cplusplus/cpp_class_member_functions.htm ) sind beide Möglichkeiten legal.

Ich bin kein Experte, aber ich denke, wenn Sie nur eine Klassendefinition in eine Datei einfügen, spielt das keine Rolle.

Wenn Sie jedoch so etwas wie eine innere Klasse anwenden oder eine Definition für mehrere Klassen haben, ist die zweite schwer zu lesen und zu pflegen.

user116541
quelle
1
Können Sie den relevanten Inhalt dieses Links in den Text Ihres Beitrags einfügen und so zukunftssicher gegen tote Links sein? Vielen Dank
JustinJDavies
2

Die erste muss in die Header-Datei eingefügt werden (in der sich die Deklaration der Klasse befindet). Die zweite kann eine beliebige Stelle sein, entweder der Header oder normalerweise eine Quelldatei. In der Praxis können Sie kleine Funktionen in die Klassendeklaration einfügen (wodurch diese implizit inline deklariert werden, obwohl letztendlich der Compiler entscheidet, ob sie inline sind oder nicht). Die meisten Funktionen haben jedoch eine Deklaration im Header und die Implementierung in einer CPP-Datei, wie in Ihrem zweiten Beispiel. Und nein, ich sehe keinen Grund, warum dies weniger lesbar wäre. Ganz zu schweigen davon, dass Sie die Implementierung für einen Typ tatsächlich auf mehrere CPP-Dateien aufteilen können.

Marius Bancila
quelle
1

Eine Funktion, die innerhalb einer Klasse definiert ist, wird standardmäßig als Inline-Funktion behandelt. Ein einfacher Grund, warum Sie Ihre Funktion außerhalb definieren sollten:

Ein Konstruktor der Klasse sucht nach virtuellen Funktionen und initialisiert einen virtuellen Zeiger, um auf die richtige VTABLE oder die virtuelle Methodentabelle zu verweisen , ruft den Konstruktor der Basisklasse auf und initialisiert Variablen der aktuellen Klasse, sodass tatsächlich einige Arbeiten ausgeführt werden.

Die Inline-Funktionen werden verwendet, wenn die Funktionen nicht so kompliziert sind und der Overhead des Funktionsaufrufs vermieden wird. (Der Overhead beinhaltet einen Sprung und eine Verzweigung auf Hardwareebene.) Und wie oben beschrieben, ist der Konstruktor nicht so einfach zu betrachten wie Inline.

R Mehta
quelle