Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Anfängerproblem: Konstruktor implementieren

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
27.03.2019, 20:38 Uhr
melli179



Hallo,

da mir bei meinem letzten Problem so super weiter geholfen wurde, versuch ich es gleich nochmal
Warscheinlich schlagen viele bei dieser Frage die Hände über dem Kopf zusammen, weil es so simpel ist,aber ich beschäftige mich schon den ganzen Tag immer wieder damit und finde weder im Vorlesungsskript, noch in diversen Youtube Videos oder ähnlichem eine konkrete Antwort. Oder ich habe einfach ein Brett vor dem Kopf.

Ich verstehe einfach nicht genau wie ich einen Konstruktor implementiere.

Hier einmal ein Beispiel und mein Denkansatz dazu.

Den Kram wie #include irgendwas und so lass ich einfach mal weg,dass ist alles klar, und schreibe einfach mal einen Codeabschnitt.


C++:

class Hund

public:
int alter;
string name;

private:
string rasse;




Bei der Implementierung muss jetzt ja der Konstruktor genauso heißen wie die Klasse, aber ich versteh nicht ob und wie ich dann die Variablen dahinter packen muss.
Hab da so viel gesehen,aber es wär für mich nie verständlich.

Entweder:


C++:

Hund::Hund ( int alter, string name, string rasse)
{ \\hier könnte ich beispielsweise\\  alter= 3 \\zuweisen\\}



oder muss ich die Variable die private ist weglassen, weil ich "von außen" eh kein Zugriff darauf habe?
Und was passiert wenn ich z.b in public noch eine Funktion wie z.B void WiemachtderHund stehen hätte??

Ich blick da echt nicht ganz durch.

In der Probeklausur habe ich folgende Aufgabe, die ich auch absolut nicht lösen kann.

"Geben Sie den benötigten Code zur Implementierung des Konstruktors außerhalb
der Klassendeklaration (z. B. in einer separaten Quellcodedatei) an."


C++:

class RegularPolygon {
public:
RegularPolygon(unsigned int number_of_sides, float side_length);
private:
unsigned int number_of_sides_;
float side_length_;
};



Hierbei würde ich sagen die Implementierung lautet:


C++:
RegularPolygon::RegularPolygon (unsigned int number_of_sides, float side_length)
{}



Oder muss ich das was unter private steht auch miteinbinden?
Und sind die Variablen in private andere als in public, weil am Ende noch ein"_" steht oder ist das nur ein Tippfehler des Professors?

Desweitern geht es in der Aufgabe um Vererbung, aber das habe ich denke ich verstanden, nur das mit der blöden Implementieren wird mir einfach nicht klar.

Vielen, vielen Dank im vorraus!!!
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
28.03.2019, 17:12 Uhr
ao

(Operator)



Zitat von melli179:

Ich verstehe einfach nicht genau wie ich einen Konstruktor implementiere.

Hier einmal ein Beispiel und mein Denkansatz dazu.


C++:

class Hund
{}
public:
int alter;
string name;

private:
string rasse;
};



Bei der Implementierung muss jetzt ja der Konstruktor genauso heißen wie die Klasse, aber ich versteh nicht ob und wie ich dann die Variablen dahinter packen muss.
Hab da so viel gesehen,aber es wär für mich nie verständlich.

Entweder:


C++:

Hund::Hund ( int alter, string name, string rasse)
{ \\hier könnte ich beispielsweise\\  alter= 3 \\zuweisen\\}



oder muss ich die Variable die private ist weglassen, weil ich "von außen" eh kein Zugriff darauf habe?


Vereinfacht gesagt ist der Konstruktor dazu da, alle Membervariablen der Klasse zu initialisieren. "Vereinfacht" deshalb, weil er auch noch andere Sachen tun kann, die hier nicht interessieren sollen.

Ziel ist, dass, nachdem der Konstruktor durchgelaufen ist, das Objekt einsatzbereit ist. D.h. man soll ALLEN Membervariablen einen sinnvollen Wert geben. Ein sinnvoller Wert kann auch 0 oder ein leerer String sein, wenn das sinnvoll ist - das kommt immer auf den Kontext an.

Die Rasse und das Alter wird man immer mit irgendwas initialisieren, weil jeder Hund irgendeine Rasse hat und irgendwann geboren ist. Aber nicht jeder Straßenköter hat einen Namen, deshalb kann beim Namen ein leerer String auch richtig sein.


Zitat:
muss ich die Variable die private ist weglassen, weil ich "von außen" eh kein Zugriff darauf habe?

Nein. Du bist ja nicht "außen". Du implementierst den Konstruktor Hund::Hund, d.h. du befindest dich von Hund::Hund bis zur schließenden Klammer (}) innerhalb der Klasse Hund und hast Zugriff auf alles Private.

Übrigens, warum sind alter und name public und rasse private? Grundsätzlich sollten alle Membervariablen privat sein, das ist das sogenannte "Geheimnisprinzip".
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
28.03.2019, 17:14 Uhr
ao

(Operator)



Zitat:
Bei der Implementierung muss jetzt ja der Konstruktor genauso heißen wie die Klasse, aber ich versteh nicht ob und wie ich dann die Variablen dahinter packen muss.
Hab da so viel gesehen,aber es wär für mich nie verständlich.


Vermutlich hast du auch sowas hier gesehen:

C++:
Hund::Hund ( int alter_, string name_, string rasse_)
: alter(alter_)
, name(name_)
, rasse(rasse_)
{ }


Also dass die Member zwischen der Kopfzeile und dem Codeblock initialisiert werden. Das ist eine sog. Initialisierungsliste, und so soll man es eigentlich machen.

Wenn der Hund Membervariablen hat, die selbst Objekte mit Nicht-Default-Konstruktor sind, dann MUSS man das sogar so machen. Siehe folgendes Beispiel:

C++:

class Fell
{
    std::string farbe;
    
public:

    Fell (std::string farbe_)
    : farbe(farbe_)
    {}
};

class Hund
{
    std::string name;
    Fell fell;
    
public:

    Hund (std::string name_, std::string farbe_)
    : name (name_)
    , fell (farbe_)
    {}

    /* gleich mehrere Compilerfehler:
    Hund (std::string name_, std::string farbe_)
    <--- hier: fehlender Konstruktor-Aufruf für Member fell
    {
        name = name_;
        fell = farbe_;    <--- hier: Klasse Fell hat keinen =-Operator für string (inkompatible Zuweisung)
    }
    */

    
};

int main ()
{
    Hund bello ("Bello", "schwarz");
}


Der Hund hat ein Fell, und das Fell hat eine Farbe. Beim Erzeugen eines Hund-Objekts wird auch das Fell-Objekt erzeugt und muss eine Farbe zugewiesen kriegen.

Der auskommentierte Konstruktor tut das nicht richtig, er versucht das erst später im Codeblock. Dann ist es aber zu spät.

Dieser Post wurde am 28.03.2019 um 17:18 Uhr von ao editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
28.03.2019, 17:33 Uhr
ao

(Operator)



Zitat:


C++:
class RegularPolygon {
public:
RegularPolygon(unsigned int number_of_sides, float side_length);
private:
unsigned int number_of_sides_;
float side_length_;
};



Hierbei würde ich sagen die Implementierung lautet:


C++:
RegularPolygon::RegularPolygon (unsigned int number_of_sides, float side_length)
{}



Oder muss ich das was unter private steht auch miteinbinden?
Und sind die Variablen in private andere als in public, weil am Ende noch ein"_" steht oder ist das nur ein Tippfehler des Professors?


Die Variablen unter private: sind Membervariablen der Klasse, also Eigenschaften des Objekts. Jede Instanz von RegularPolygon, die du erzeugst, hat ihren eigenen Satz dieser Eigenschaften, und NUR DIESE werden im Konstruktor initialisiert.

Am Ende des Namens ein "_" anzuhängen ist eine von vielen denkbaren Schreibkonventionen. Eine andere populäre ist, ein "m_" voranzustellen (für "Member").

Die "Variablen" unter public: sind KEINE Klassen-Member. Es sind formale Parameter des Konstruktors, genau wie die Parameter einer gewöhnlichen Funktion. Die heißen oft sehr ähnlich wie die Member, wenn sie dazu benutzt werden, den Members Werte zuzuweisen.

Die RICHTIGE Implementierung ist:

C++:
RegularPolygon::RegularPolygon (unsigned int number_of_sides, float side_length)
: number_of_sides_(number_of_sides)
, side_length_(side_length)
{}


, also wieder mit Initialisierungsliste.

Eine andere Implementierung ist:

C++:
RegularPolygon::RegularPolygon (unsigned int number_of_sides, float side_length)
{
    number_of_sides_ = number_of_sides;
    side_length_ = side_length;
}


Die macht in diesem Fall am Ende auch das Richtige, weil die Member von trivialem Typ sind und die Zuweisung definiert ist. Schön ist es aber nicht, und wenn der Objektaufbau komplizierter wird, ist es unter Umständen auch nicht mehr richtig.

Euer Prof sollte das aber eigentlich wissen und euch richtig beibringen.

Dieser Post wurde am 28.03.2019 um 17:34 Uhr von ao editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
28.03.2019, 21:54 Uhr
melli179



Vielen Dank für die ausführliche Erklärung!!!!
Nach ein paar Mal durchlesen habe ich es nun glaub ich verstanden.

Eigentlich ist das gar nicht so schwierig, man muss nur darauf achten wo man was einsetzt.

Die Aufgabe lautet weiter:

Leiten Sie von dieser Klasse eine neue Klasse EquilateralTriangle für gleichseitige Dreiecke ab. Etwaige öffentliche Methoden und Attribute der Basisklasse sollen in der
abgeleiteten Klasse ebenfalls öffentlich sein. Geben Sie die dazu benötigte Klassendeklaration
samt Implementierung des Konstruktors an

Dies würde ich so lösen:


C++:

class EquilateralTriangle: public RegularPolygon
{
// Muss hier der ganze Quatsch der mit side_length und number_of sides nochmal rein??
}


Oder kann ich die Klasse im Prinzip auch leer lassen?
Weil in der Aufgabe ja steht, dass die Methoden aus der Basisklasse übernommen werden und da ich ja eine public Vererbung gemacht habe, kann EquilateralTriangle ja auf die Sachen zugreifen.

Die Implementierung "ohne Inhalt" wäre dann doch nur


C++:

EquilateralTriangle:: EquilateralTriangle()
{}



und wenn ich die gleichen Inhalte wie in RegularPolygon hätte:


C++:

EquilateralTriangle:: EquilateralTriangle (unsigned int number_of_sides, float side_length,string farbe)
: number_of_sides_(number_of_sides),
side_length_(side_length),
farbe_(farbe)
{}



Aber das wäre ja eigentlich nur doppelte Schreibart oder??

Und was ich auch noch nicht raus habe, ist wie ich wenn ich für die beiden Variablen Wert einsetze
also z.B


C++:
int main ()
{

RegularPolygon test(4,8.5);
return 0;
}


die Werte auch ausgegeben bekomme, hab es über einen cout Befehl versucht,aber das hat nicht funktioniert.

Vielen, vielen Dank für die großartige Hilfe !!

Dieser Post wurde am 06.04.2019 um 21:01 Uhr von FloSoft editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
29.03.2019, 10:18 Uhr
ao

(Operator)



Zitat:
class EquilateralTriangle: public RegularPolygon
{
// Muss hier der ganze Quatsch der mit side_length und number_of sides nochmal rein??
}

Nein, gerade nicht, denn er ist ja von RegularPolygon geerbt.

Überleg aber mal, ob es sinnvoll ist, im EquilateralTriangle-Konstruktor die number_of_sides als Parameter zu haben.

Und um in EquilateralTriangle auf die Eigenschaften von RegularPolygon (number_of_sides, side_length, farbe) zugreifen zu können, müssen diese in RegularPolygon protected sein, nicht private.


Zitat:
... die Werte auch ausgegeben bekomme, hab es über einen cout Befehl versucht,aber das hat nicht funktioniert.

Was genau hast du versucht? cout << test.number_of_sides in der main? Das kann nicht klappen, weil die Member private (bzw. protected) sind, da kommst du von außen nicht dran. Dazu müsstest du öffentliche Getter-Methoden schreiben.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
29.03.2019, 12:57 Uhr
ao

(Operator)


Huch, ich versteh nicht, warum das hier eingerückt ist ....


Jedenfalls muss man hier im Konstruktor von EquilateralTriangle eine Initialisierungsliste schreiben, die die Basisklasse korrekt aufsetzt, mit irgendwelchem Code im Konstruktor-Block gehts hier nicht mehr:

C++:
class RegularPolygon
{
protected:
    unsigned int number_of_sides_;
    float side_length_;

public:
    RegularPolygon(unsigned int number_of_sides, float side_length)
    : number_of_sides(number_of_sides_)
    , side_length(side_length_)
    {}
};

class EquilateralTriangle : public RegularPolygon
{
public:
    EquilateralTriangle(float side_length)
    : RegularPolygon(3, side_length)
    {}

};

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
29.03.2019, 13:17 Uhr
ao

(Operator)


Und man kann sehr geteilter Meinung darüber sein, ob das hier eine sinnvolle Anwendung für Vererbung ist. Will man das wirklich fortführen und für jedes regelmäßige N-Eck (Quadrat, Pentagon, Sechskantmutter, 50-Pence-Münze, Stopschild, Uhr-Zifferblatt) eine eigene Klasse definieren? Der Nutzen davon ist doch eher gering.

Ja, es ist eine "Ist-ein"-Beziehung, aber man muss sich hüten, jede Ist-ein-Beziehung reflexartig mit einer Ableitung zu modellieren. Da kommt man sehr schnell auf Klassenhierarchien, die derart groß und kompliziert sind, dass kein Mensch mehr durchblickt.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
29.03.2019, 14:04 Uhr
melli179



Danke für deine erneute Hilfe!

Ich glaub im groben habe ich das jetzt verstanden.
Ich glaube diese Aufgabe war auch nicht das beste Bespiel um bestimmte Sachen zu erklären, da wie du sagt die Logik dahinter manchmal nicht so sehr gegeben ist.

Mit der Bildschirmausgabe über die Get/Set- Methode habe ich es nun auch hinbekommen!
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: