Wozu Destruktor

06/29/2016 14:23 NRj™#1
Hallo,

ist es sinnvoll in C++ nach einem Konstruktor auch den Destruktor zu programmieren und aufzurufen? Ist dachte der würde automatisch aufgerufen werden?
06/29/2016 14:39 Dr. Coxxy#2
die explizite implementation eines dekonstruktors kann in vielen fällen nötig sein, halt um dinge aufzuräumen die im verlauf des objekts oder im konstruktor erstellt wurden.
der destruktor wird genauso "automatisch" aufgerufen wie der konstruktor.
konstruktor beim new oder explizit, dekonstruktor beim delete oder explizit - wobei die jeweiligen expliziten aufrufe meist zeichen für fehlerhaftes design sind.
06/29/2016 16:08 NRj™#3
Okay verstehe...
Ich habe bei meinem Projekt gerade ein ganz anderes Problem:
Bekomme den Fehler: ' ... ' was not declared in this scope (also meine Funktionen)
Woran kann das liegen?

main:
Bruch.cpp
Bruch.h
06/29/2016 17:16 th0rex#4
Die print Funktion die du in Bruch.cpp definiert hast ist nicht Teil der Klasse, deswegen können auch die Variablen nicht gefunden werden.
06/29/2016 17:32 NRj™#5
Quote:
Originally Posted by C0untLizzi View Post
Die print Funktion die du in Bruch.cpp definiert hast ist nicht Teil der Klasse, deswegen können auch die Variablen nicht gefunden werden.
Ups. Da ist etwas beim Kopieren verloren gegangen. Ich habe jetzt in der Bruch.cpp wieder das Bruch:: vor die Funktion angefügt. An der Fehlermeldung ändert das nichts. Das Problem hab ich bei allen definierten Funktionen.
06/29/2016 18:07 th0rex#6
In main müsstest du b1->print() aufrufen wenn du print als Funktion der Klasse definiert hast. Btw würde die genaue Fehlermeldung schon helfen.
06/29/2016 18:51 NRj™#7
Quote:
Originally Posted by C0untLizzi View Post
In main müsstest du b1->print() aufrufen wenn du print als Funktion der Klasse definiert hast. Btw würde die genaue Fehlermeldung schon helfen.
Danke. Das wars! :)
07/07/2016 15:22 +Yazzn#8
Heir bietet es sich an semantisch sinnvoll die Operatoren zu überladen. Das ganze würde dann in etwa so aussehen.

Code:
// https://github.com/gcc-mirror/gcc/blob/edd716b6b1caa1a5cb320a8cd7f626f30198e098/libstdc%2B%2B-v3/include/experimental/numeric#L55
template<typename _Mn, typename _Nn>
constexpr std::common_type_t<_Mn, _Nn>
gcd(_Mn __m, _Nn __n) {
	static_assert(std::is_integral<_Mn>::value, "arguments to gcd are integers");
	static_assert(std::is_integral<_Nn>::value, "arguments to gcd are integers");

	return __m == 0 ? std::abs(__n)
		: __n == 0 ? std::abs(__m)
		: /*fundamentals_v2::*/gcd(__n, __m % __n);
}

class fraction
{
public:
  fraction(int numerator, int denominator)
    : numerator(numerator), denominator(denominator)
  {}

  friend std::ostream& operator<<(std::ostream& os, const fraction& f);

  fraction &reduce() {
    fraction tmp(*this);
    tmp /= gcd(numerator, denominator);
    return tmp;
  }
  
  fraction &reciprocal() {
    fraction r(denominator, numerator);
    return r;
  }

  fraction &operator*=(const fraction &rhs) {
    numerator *= rhs.numerator;
    denominator *= rhs.denominator;
    return *this;
  }

  fraction &operator*=(int factor) {
    numerator *= factor;
    // denominator *= 1
    return *this;
  }

  fraction &operator/=(const fraction &rhs) {
    *this *= rhs.reciprocal()
    return *this;
  }

  fraction &operator/=(const int factor) {
    denominator *= factor;
    return *this;
  }

  fraction &operator+=(const fraction &rhs) {
    numerator = (numerator * rhs.denominator) + (rhs.numerator * denominator);
    denominator = denominator * rhs.denominator;
  }

  fraction &operator+=(int factor) {
    numerator += denominator;
    return *this;
  }
  
  operator int() const
  { return static_cast<int>(static_cast<float>(*this)); }
  
  operator float() const
  { return static_cast<float>(numerator) / static_fast<float>(denominator); }
  
private:
  int numerator;
  int denominator;
}

std::ostream& operator<<(std::ostream& os, const fraction& f) {
  os << f.numerator << '/' << f.denominator;
  return os;
}
07/07/2016 20:37 KleinAnorld#9
sehr gut ausgeführt von Dr. Coxxy, wenn du viel mit dem heap also pointer und neu erzeugten feldern arbeitest bietet es sich zb sehr gut dort an diesen wieder sauber zu entladen und diese felder wieder frei zugeben, bei bearbeiten von großen datenmengen ist eine saubere verwaltung des speichers mit das wichtigste sonst sprengt dir die anwendung den rahmen deines speichers :D
07/11/2016 15:43 MrSm!th#10
Quote:
Originally Posted by Dr. Coxxy View Post
die explizite implementation eines dekonstruktors kann in vielen fällen nötig sein, halt um dinge aufzuräumen die im verlauf des objekts oder im konstruktor erstellt wurden.
der destruktor wird genauso "automatisch" aufgerufen wie der konstruktor.
konstruktor beim new oder explizit, dekonstruktor beim delete oder explizit - wobei die jeweiligen expliziten aufrufe meist zeichen für fehlerhaftes design sind.
Quote:
Originally Posted by NRj™ View Post
Okay verstehe...
Ich habe bei meinem Projekt gerade ein ganz anderes Problem:
Bekomme den Fehler: ' ... ' was not declared in this scope (also meine Funktionen)
Woran kann das liegen?

main:
Bruch.cpp
Bruch.h
Wichtig ist aber auch, dass C++ eben nicht gleich Java ist. In C++ verwendest du das new-Schlüsselwort wesentlich seltener (bis hin zu gar nicht, seit C++11/14) als in Java, wo damit jedes Objekt erzeugt wird. new/delete wird nur für Objekte auf dem Heap verwendet (und auch da solltest du lieber zu Smartpointern und den entsprechenden Hilfsfunktionen greifen, um new/delete zu umgehen), für Objekte auf dem Stack ist das nicht notwendig. Letztere sind wenn möglich zu bevorzugen, weil du dich dann nicht darum kümmern musst, den Speicher wieder freizugeben - das passiert bei Objekten auf dem Stack automatisch. Der Konstruktor wird auch nicht nur bei new aufgerufen und der Destruktor nicht nur bei delete, sondern eben auch beim Auf- und Abbau des Stackframes.

Dein Code enthält (aufgrund o.g. Thematik) ein Memory Leak (auch wenn es in deinem Programm keine Auswirkung hat, weil das System beim Beenden des Programms den Speicher wieder freigibt). Du reservierst Speicher mit new, aber gibst ihn mit delete nicht mehr frei. Das solltest du auf jeden Fall vermeiden.
Zwar könntest du das beheben, indem du ans Ende der main die Objekte per delete wieder zerstörst, aber folgender Code wäre ebenfalls möglich (und besser):

Code:
#include <iostream>
#include "Bruch.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;

int main(int argc, char** argv) {
    Bruch b1(2, 1), b2(2, 1); //alternativ: b1{2, 1} und b2{2, 1}
    b1.print();
    return 0;
}
07/18/2016 18:04 +Yazzn#11
Quote:
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
wat?

> int main(int argc, char** argv)

Quote:
Originally Posted by n4594 -- 3.6.1 Main Function -- basic.start.main
An implementation shall allow both
(2.1) — a function of () returning int and
(2.2) — a function of (int, pointer to pointer to char) returning int
> return 0;

Quote:
Originally Posted by n4594 -- 3.6.1 Main Function -- basic.start.main
A return statement in main has the effect of leaving the main function (destroying any objects with automatic
storage duration) and calling std::exit with the return value as the argument. If control flows off the end
of the compound-statement of main, the effect is equivalent to a return with operand 0 (see also 15.3).
pls
07/19/2016 22:37 MrSm!th#12
Was willst du uns mitteilen?
07/21/2016 18:09 +Yazzn#13
Dazu bedarf es keines weiteren Kommentars, es sollte hinlänglich ersichtlich sein.
07/21/2016 19:25 Dr. Coxxy#14
um ehrlich zu sein ich check auch nicht was du uns sagen willst - die offensichtliche antwort wäre, dass du entweder ziemlich dämlich bist, oder ich dich missverstanden habe - Mr. Smiths freundliche auslegung war halt letztere :)
07/21/2016 19:55 Jeoni#15
Ich vermute, dass P.File auf Folgendes hinaus wollte:
a) es wurde im Beispielcode explizit system("pause") empfohlen (hier bin ich mir nicht sicher, ob dass wirklich Ziel der Nachricht / Kritik war),
b) "int main()" als Signatur des Einstiegspunktes reicht völlig aus, weil es standardkonform ist und auf argc und argv nicht zugegriffen wird und
c) "return 0" am Ende von main ist unnötig, weil das durch den Standard schon implizit gegeben ist.
Generell ist das sicher alles korrekt, wobei ich den Vorteil von b und c nicht erkennen kann, außer, dass die Source-Datei etwas weniger Bytes hat. Gut, bei b kann der Compiler noch Optimierungen machen, so dass argc / argv gar nicht vom Betriebssystem geholt werden muss, aber das halte ich eher für eine kleinere Optimierung und könnte sogar in der aktuellen Form geschehen, weil der Compiler "merkt", dass argc / argv nie benutzt werden.
Mit freundlichen Grüßen
Jeoni