2014-09-26 10 views
6

Wenn nur diese Methode definiert wird, wird ein Compilerfehler auftreten.Warum muss eine Methode in einer C++ - Klassendefinition deklariert werden?

void classA::testMethod() {  
} 

Und so muss es zuerst deklariert werden:

class classA {  
    void testMethod(); 
}; 

Warum sollten diese deklariert werden?

Ich weiß, für übliche Verfahren C Verfahren nicht declare benötigt wird, aber sie können nur definiert werden:

void test() { 
} 
+0

können Sie Funktionen außerhalb einer Klasse wie in c auch – jonynz

+0

@jonynz: Das macht keinen Sinn, C hat keine Klassen oder Methoden. – MSalters

+0

Punkt genommen, danke – jonynz

Antwort

7

Es gibt mehrere sekundäre Gründe objektive und subjektive (dh ermöglicht die Sichtbarkeit angeben, dient als Schnittstelle für die Klasse und wahrscheinlich mehrere andere im Zusammenhang mit dem Zusammenstellen und verknüpfen Phasen und TU-Symbole Sichtbarkeit, keine Klasse ist die grundlegende Einkapselung Einheit mit allen zu erwähnen, die impliziert), aber ein unbestreitbaren ist, dass der Standard diktiert es:

N3797 - class.mfct/p2

A Elementfunktion kann definiert werden (8.4) in i ts Klassendefinition, in Fall ist es eine Inline-Member-Funktion (7.1.2), oder es kann außerhalb seiner Klassendefinition definiert werden, wenn es bereits deklariert, aber nicht definiert in seiner Klassendefinition. Eine Memberfunktion Definition, die außerhalb der Klassendefinition erscheint, soll in einem Namensraumbereich erscheinen, der die Klassendefinition einschließt. Mit Ausnahme von Member Funktionsdefinitionen, die außerhalb einer Klassendefinition auftreten, und mit Ausnahme der expliziten Spezialisierungen von Memberfunktionen der Klasse Vorlagen und Memberfunktionsvorlagen (14.7), die außerhalb der Klassendefinition auftreten, wird eine Memberfunktion nicht neu deklariert.

Schwerpunkt meiner.

+1

Keine Sorge, danke, dass du das geschafft hast! –

6

Es hilft Verkapselung. Wenn Sie eine Klasse haben eine

class A { 
public: 
    foo(); 
    bar(); 
} 

können Sie sicher sein, dass nur Methoden foo und bar Durcheinander mit den privaten Datenelemente der Klasse. (Oder Zeiger Magie oder undefined Verhalten natürlich)

+0

Ich sehe nicht, wie das zusammenhängt. – user2864740

+0

@ user2864740 Es wäre möglich, die freistehende 'void classA :: testMethod() {...}' -Form so zu definieren, dass Sie die Klasse erweitern möchten. In C++ ist das nicht der Fall. – Csq

+0

Was hat die Kapselung damit zu tun? C++ * könnte * so definiert worden sein, dass er einige dumme Zaubersprüche ausführt, wie etwa die Verbreitung solcher gefundenen Definitionen in Klassendefinitionen (was wenig sinnvoll ist, insbesondere, da Definitionen oft von der Implementierung getrennt sind). Aber nichts davon betrifft die Verkapselung. – user2864740

1

why should declare these? I know for common c method, there is no need to declare, instead of it just define it:

Es gibt keine Methode in C, nur Attribute eines struct kann wich Funktion Pointeur sein, dann zu einem Funktions addresse verbunden.

Darüber hinaus haben Sie es in der Klassendefinition aus dem gleichen Grund Sie es in C tun, um zu erklären:

Die compilateur verwandeln diese Voranmeldung in eine Funktion pointeur dann zu der genannten Methode assoziieren int den Bau von dein Objekt.

Wenn eine Definition einer C++ Klasse sollte auf eine C-Struktur transformiert werden würde der Code wie folgt sein:

struct Aclass { 
void (*Amethode))(int); 
} 
void Amethode(int) { return (0); } 
Aclass primaryObject = {&Amethode}; 
Aclass* AclassConstructor() { 
Aclass* object; 
object = malloc(sizeof(Aclass)); 
memcpy(object, primaryObject, sizeof(Aclass)); 
return (object); 
} 
+1

Behauptest du, dass Memberfunktionen in C++ in Form von Funktionszeigerattributen implementiert sind? Dies ist nicht der Fall. Memberfunktionen sind statisch definiert und vom Compiler statisch aufgelöst. Funktionszeiger treten bei der Verwendung von virtuellen Funktionen ein, aber auf eine andere Weise als das, was ich zu beschreiben glaube. –

+0

@Magnus Hoff In Fakten hast du recht, es ist nicht der Fall, aber es ist einfacher für einen debutierenden Programmierer zu verstehen, oder ich denke schon ... – CollioTV

+1

Während das Schema, das du beschreibst, funktionieren würde, repräsentiert es nicht, was tatsächlich passiert und ist somit verwirrend. Ich denke, das Folgende ist viel einfacher zu verstehen, zusätzlich zu einer viel näheren Darstellung dessen, was tatsächlich los ist: http://ideone.com/4xhr8T –

8

Sie müssen nicht um die Methode zu erklären, bevor Sie es definieren, aber Sie müssen Deklarieren Sie Klassenmethoden in der Klasse. Sonst wäre es keine Klassenmethode.

Das mag wie ein Widerspruch erscheinen, aber eine Definition ist auch eine Erklärung. Also, was das bedeutet, ist, dass die Definition in der Klasse erscheint selbst:

class A { 
    void testMethod() { /*...*/ } 
}; 

[Bearbeiten] auch praktisch gesehen, innerhalb der Klassendeklaration gibt es private, protected und public Teile. Dies wird für die Kapselung benötigt. Wenn Sie Methoden außerhalb der Klasse deklarieren könnten, würden Sie die Kapselung verlieren. Jeder könnte auf private Mitglieder zugreifen, indem er zusätzliche Getter und Setter definiert, selbst wenn diese keinen Sinn ergeben. Klasseninvarianten würden bedeutungslos werden.

+0

@TonyD Sie können andere Fragen nicht ablehnen, um eine andere Antwort zu bekommen, das ist Ihre persönliche Meinung und andere stimmen vielleicht nicht überein. Jetzt verstehe ich, wer meine Antwort auch abgelehnt hat. Sie sollten die Antwort ablehnen, wenn die Antwort falsch/unbrauchbar ist, nicht, wenn Sie denken, dass es eine bessere oder Ihre bevorzugte Antwort gibt. –

+0

"Wenn Sie Methoden außerhalb der Klasse deklarieren könnten, würden Sie die Kapselung verlieren." Natürlich können Sie dieses Problem umgehen, indem Sie Zugriffsmodifizierer für jede Methodendefinition auf Java zulassen. –

2

"Methoden" oder "Member-Funktionen" (wie die üblichere Terminologie in C++) sind Teil der Klassendeklaration. Da Sie eine C++ - Klasse an einem einzigen Ort deklarieren müssen, müssen Sie sicherstellen, dass alle "Mitgliedsfunktionen" (oder "Methoden") bereits in dieser Deklaration vorhanden sind.

Ich weiß, für übliche Verfahren C Verfahren nicht declare benötigt wird, aber sie kann nur

definiert werden, wenn Sie eine „gemeinsame C-Methode“ in C++ beziehen, meinen Sie eigentlich eine „gemeinsame Funktion". Beachten Sie, dass Sie Klassen überall dort deklarieren können, wo Sie solche Funktionen deklarieren können.

Beachten Sie auch, dass Sie eine Elementfunktion mit einem Rumpf deklarieren können. Sie müssen Deklaration und Definition nicht trennen. I.e. das ist vollkommen gültig:

class A{ 
    void privateMethod() { 
     // do something here... 
    } 
public: 
    void publicMethod() { 
     // do something here... 
    } 
}; 
2

Beachten Sie die Verwendung der Qualifier ::. Es bedeutet

  • auf der linken Seite Sie einen Namespace oder Klassenkennung
  • auf der rechten Sie einen Namespace, die Klasse oder Methode/Funktion Kennung

So schreibt void A::testMethod() geht davon aus, dass es eine haben müssen, ist Klasse oder Namespace A definiert - so ist C++ definiert. Und dies gilt für

void A::testMethod(); 

sowie

void A::testMethod() 
{ 
} 

auch den globalen Namensraum beachten Sie, wo Sie auf der linken Seite von :: in der Tat nichts als in

void ::testMethod() 
{ 
} 

und per Definition der Der globale Namespace ist immer definiert, so dass der obige Code eine Funktion ähnlich dem C-Stil ohne das Qualifikationsmerkmal definiert.

2

Auch wenn es vom Standard nicht vorgeschrieben war, gibt es die folgenden zwei Gründe, warum Sie alle Klassenmethoden in der Klassendefinition deklarieren müssen.

  1. Sie können nur etwas als öffentlich erklären, privat oder in der Klassendeklaration geschützt, können Sie nicht in der CPP-Datei so bei der Methodendefinition tun. Welche Sichtbarkeit hat Ihre freistehende Klassenmethode?

  2. Wenn der Standard entschieden hat, einen der drei als Standard (C++ ist standardmäßig auf privat) zu wählen und diese Sichtbarkeit auf Ihre Methode zu legen, haben Sie jetzt ein Problem mit der Bereichsdefinition. Selbst die restriktivste Sichtbarkeit (privat) bedeutet, dass Sie die Methode in jeder anderen Mitgliedsmethode, einschließlich der in der Quelldatei definierten Quellmethode, verwenden können. Ohne die Deklaration in der Klassendefinition wären diese früheren Funktionen Ihrer freien Methode nicht bekannt, sodass Sie gegen die Bereichsdefinitionen verstoßen.

In Ihrem foo.h Header:

class foo 
{ 
public: 
    foo() {} 
    virtual ~foo() {} 

    declaredMethod(); 
}; 

In Ihrem foo.cpp

foo::declaredMethod() 
{ 
    ... 
    freeStandingMethod(); // This should be legal, but it can't work since we 
          // haven't seen foo::freeStandingMethod() yet 
    ... 
} 

foo::freeStandingMethod() 
{ 
    ... 
} 

Auch wenn Sie diese Arbeit in der gleichen CPP-Datei machen könnte, dann ist es legal Platz foo::freeStandingMethod() in einer anderen CPP-Datei von foo::declaredMethod() zu diesem Zeitpunkt wird dies unmöglich.

2

Dies ist keine Antwort, aber Sie könnten es informativ und lustig finden. Hinzufügen von 2 Template-Funktionen zu Ihrer Klasse effektiv können Sie jede freie Funktion aufrufen, die ein Objekt dieser Klasse als ersten Parameter nimmt:

#include <string> 
#include <iostream> 

struct puppy { 
    puppy(std::string name) 
    : _name(std::move(name)) 
    {} 

    const std::string& name() const noexcept { 
     return _name; 
    } 

    void set_name(std::string name) { 
    _name = std::move(name); 
    } 

    template<class F, class ...Args> 
     auto perform(F&& f, Args&&...args) const 
     -> decltype(f(*this, std::forward<Args>(args)...)) 
    { 
     return f(*this, std::forward<Args>(args)...); 
    } 

    template<class F, class ...Args> 
     auto perform(F&& f, Args&&...args) 
     -> decltype(f(*this, std::forward<Args>(args)...)) 
    { 
     return f(*this, std::forward<Args>(args)...); 
    } 

private: 
    std::string _name; 
}; 


void woof(const puppy& p) { 
    std::cout << "puppy " << p.name() << " woofs!" << std::endl; 
} 

void indented_woof(const puppy&p, size_t indent) { 
    std::cout << std::string(indent, ' '); 
    woof(p); 
} 

void complex_woof(const puppy& p, int woofs) 
{ 
    std::cout << "attention!" << std::endl; 
    for (int i = 0 ; i < woofs ; ++i) { 
    p.perform(indented_woof, 4); 
    } 
} 

std::string name_change(puppy& p, std::string(new_name)) 
{ 
    auto old_name = p.name(); 
    p.set_name(std::move(new_name)); 
    return old_name; 
} 

int main() 
{ 
    puppy fido { "fido" }; 

    fido.perform(woof); 
    fido.perform(complex_woof, 10); 
    auto old_name = fido.perform(name_change, "bonzo"); 
    fido.perform(woof); 
    std::cout << "changed name from " << old_name << std::endl; 
    return 0; 
} 
+0

Als Antwort auf den (jetzt gelöschten) Kommentar: Eine Version von perform muss const sein, um Funktionen zu unterstützen, die einen nicht konstanten Welpen & übernehmen. –

3

Alle bisherigen Antworten richtig sind, soweit sie gehen, aber sie nicht auf den Grund für die Regel hinweisen. In C++, ist eine Klassendefinition geschlossen; Sie können später nicht hinzufügen. Diese ist für nicht statische Datenelemente erforderlich, da sie die Größe (und die implizit generierten Sonderfunktionen) bestimmen, aber es ist ein Grundprinzip in C++ für alle Klassenmitglieder: nicht nur Daten, sondern Funktionen, Typen, usw. Es gilt als wichtig für gute Kapselung.

Dies gilt für die meisten (aber nicht alle) Sprachen, die das Klassenkonzept unterstützen.

Dies erklärt auch, warum es für Namespaces anders ist (die nicht geschlossen sind).

+0

+1 (früher), aber in Fairness "Alle vorherigen Antworten" außer Csq. –

Verwandte Themen