K
Das tolle an C++ ist, dass Du bei benutzerdefinierten Typen selbst definieren kannst, was passieren soll, wenn ein Objekt dieses Typs initialisiert, kopiert und zerstört werden soll. Für bestimmte Typen wie zB std::string und std::vector ist das sehr sinvoll. Man kann sie als "Black-Box" behandeln. std::string ist etwas, was sich ähnlich wie int oder double verhält bzgl Kopieren und Zuweisen. Man kann Variaben davon anlegen und diese bestimmte Werte speichern lassen. Im Falle von std::string sind das eben Zeichenketten statt Ganzzahlen oder Fleißkommazahlen. Intern wird dafür irgendwie dynamisch Speicher reserviert, ja. Das kann einem aber eigentlich egal sein, wie das genau funktioniert. Benutzen kann man diesen Typ trotzdem.
Wenn Du eine Klasse eine Resource (wie zB dynamisch allozierter Speicher) verwalten lassen möchtest, brauchst Du die "großen Drei":
class string_uebung
{
public:
...
/// steuert, wie man string-objekte kopiert
string_uebung(string_uebung const& x);
/// steuert, was bei Zuweisungen passieren soll
string_uebung& operator=(string_uebung const& x);
/// steuert, was passieren soll, wenn das Objekt zerstört wird
~string_uebung();
...
};
Definiert man diese Operationen nicht selbst, werden sie vom Compiler generiert, der einfach elementweise kopiert/zuweist und nichts besonderes im Destruktor macht. Das sollte eigentlich auch der Regelfall sein. Wenn Du es für nötig hältst, bei einem großteil Deiner Klassen diese Funktionen selbst zu definieren, machst Du etwas falsch; denn Du kannst "größere" Klassen aus einfacheren, kleinen zusammensetzen, die jeweils etwas von sich aus Verwalten können (wie auch immer).
schlecht:
struct person
{
char* vorname;
char* nachname;
person(const char* v, const char* n)
: vorname(strdup(v))
, nachname(strdup(n))
~person() { free(vorname); free(nachname); }
// Oops! Der Programmierer hat auch Kopierkonstruktor
// und Zuweisungsoperator vergessen!!!
// So kopiert der Compiler nur die rohen Zeiger alleine!
};
gut:
struct person
{
string vorname;
string nachname;
};
Wenn hier ein person-Objekt kopiert wird, dann wird elementweise kopiert. In diesem Fall ist das aber okay; denn string ist kein dummer Zeiger. Der Kopierkonstruktor der string-Klasse wird aufgerufen und der macht schon das richtige. Du solltest also, wenn Du die großen Drei selbst definierst, dich höchstens nur um eine Resource manuell kümmern in der Klasse. Alles andere kannst Du aus "schlauen" Objekten zusammensetzen, sodass Du die großen Drei so gut wie nie selbst definieren musst.