Objekte auf dem Heap erzwingen
-
Hallo,
ich habe eine Klasse A, die Zeiger auf Objekte einer abstrakten Basisklasse B in einem std::vector speichert. Wenn ein Objekt der Klasse A zerstört wird, möchte ich auch alle Objekte zerstören, auf die die Zeiger in dem vector verweisen. Nun habe ich aber keine Ahnung, ob die Objekte auf die die Zeiger verweisen mit new erzeugt wurden oder nicht, d.h. ich weiss nicht, ob ich delete auf dem Objekt aufrufen muss oder nicht.
Gibt es eine Technik, die es mir erlaubt zu erzwingen, dass nur mit new erzeugte Objekte an die Hinzufügen-Funktion meiner Klasse A übergeben werden können und damit alle meine Zeiger auf dynamisch reservierten Speicher zeigen?Gruß
void*
-
Um die Heap-Speicherung zu erzwingen versteckt man den Destruktor. Damit man die Klasse noch als
Basisklasse verwenden kann, wird der Destruktor jedoch nicht hinter private versteckt, sondern hinter
protected. Nun erlaubt der Compiler keine direkte Konstruktion mehr, da die automatische Destruktion
nicht mehr möglich ist (diese wird ja bei dem Verlassen des Gültigkeitsbereiches ausgeführt), weil der
Destruktor von außerhalb nicht mehr aufgerufen werden kann. Das Objekt kann also nur noch mit new
erzeugt werden. Für die manuelle Zerstörung des Objektes mit delete wird nun eine Destroy()-
Methode bereitgestellt.
-
Helmut S. schrieb:
Um die Heap-Speicherung zu erzwingen versteckt man den Destruktor. Damit man die Klasse noch als
Basisklasse verwenden kann, wird der Destruktor jedoch nicht hinter private versteckt, sondern hinter
protected. Nun erlaubt der Compiler keine direkte Konstruktion mehr, da die automatische Destruktion
nicht mehr möglich ist (diese wird ja bei dem Verlassen des Gültigkeitsbereiches ausgeführt), weil der
Destruktor von außerhalb nicht mehr aufgerufen werden kann. Das Objekt kann also nur noch mit new
erzeugt werden. Für die manuelle Zerstörung des Objektes mit delete wird nun eine Destroy()-
Methode bereitgestellt.das hat allerdings nicht den gewünschten effekt. da die basisklasse abstrakt ist, kann sowieso kein objekt dieser klasse (als automatisches oder heap objekt) erzeugt werden. und die frage, ob der destruktor oder die konstruktoren nun protected isnd oder public, spielt dann eben keine rolle, wenn ein objekt einer abgeleiteten klasse erzeugt wird. denn entscheidend sind dann die zugriffrechte auf die konstruktoren/den destruktor in der abgeleiteten klasse. tatsächlich kenne ich keinen weg, der verhindert, dass das erzeugen eines automatischen objektes einer abgeleiteten klsse zu einem compilerfehler führt (ok, es gäbe die möglichkeit, die besagten zeiger auf die bewußten objekte in eine klasse zu verpacken, die ihrerseits nur in bestimmten factoryfunktionen erzeugt werden kann - dann müsste man nur dafür sorgen, dass sich diese factories an die vereinbarung halten). es ist denkbar, in diesem falle einen laufzeitfehler zu erzeugen: in dem man operator new/delete überlädt und z.b. den erzeugten pointer registriert. der konstruktor der basisklasse könnte dann überprüfen, ob this in einen registrierten bereich fällt oder nicht.
-
class Foo { private: Foo(); public: static Foo* create() { return new Foo(); } };edit: Muss natürlich noch static hin...
Gruß
-
Hallo,
vielen Dank für die Antworten und die darin enthaltenen Ideen. Es funktioniert 1a.
Gruß
void*PS: camper, du bist doch nicht etwa ein CS-Camper?

-
camper schrieb:
Helmut S. schrieb:
Um die Heap-Speicherung zu erzwingen versteckt man den Destruktor. Damit man die Klasse noch als
Basisklasse verwenden kann, wird der Destruktor jedoch nicht hinter private versteckt, sondern hinter
protected. Nun erlaubt der Compiler keine direkte Konstruktion mehr, da die automatische Destruktion
nicht mehr möglich ist (diese wird ja bei dem Verlassen des Gültigkeitsbereiches ausgeführt), weil der
Destruktor von außerhalb nicht mehr aufgerufen werden kann. Das Objekt kann also nur noch mit new
erzeugt werden. Für die manuelle Zerstörung des Objektes mit delete wird nun eine Destroy()-
Methode bereitgestellt.das hat allerdings nicht den gewünschten effekt. da die basisklasse abstrakt ist, kann sowieso kein objekt dieser klasse (als automatisches oder heap objekt) erzeugt werden.
Mehr oder weniger hat es den gewünschten Effekt, ich habe es gerade ausprobiert:
Das man von der Klasse kein Objekt erstellen kann, weil sie abstrakt ist, ist klar. Es sollen aber auch von den abgeleiteten Klassen keine Objekte auf dem Stack, d.h. nur welche auf dem Heap erzeugt werden können.
Folgender Beispiel-Code für die Klassendeklarationen:
class Basis { public: Basis(); virtual void Delete(void); virtual int AbstractFunc(void) = 0; protected: virtual ~Basis(); private: }; class Erbe : public Basis { public: Erbe(); virtual void Delete(void); virtual int AbstractFunc(void); protected: virtual ~Erbe(); };Wenn man diese Klassendeklarationen benutzt, funktioniert folgender Code (wie gewünscht) NICHT :
int main(void) { Erbe testobjekt; return(0); }"ComeauTest.c", line 24: error: "Erbe::~Erbe()" is inaccessible Erbe testobjekt; ^ 1 error detected in the compilation of "ComeauTest.c".Folgendes funktioniert aber:
int main(void) { Erbe *testzeiger = new Erbe(); return(0); }Zwei Probleme gibt es aber immer noch:
-
Der Destruktor muss auch in allen abgeleiteten Klassen konsequent protected gehalten werden.
-
Folgender Code funktioniert komischerweise, da würde ich auch um Erklärung bitten:
int main(void) { Erbe testobjekt(); return(0); }Aber (wie oben schon gesagt) funktioniert folgendes NICHT:
int main(void) { Erbe testobjekt; return(0); }Der einzige Unterschied sind die Klammern nach dem Objektnamen zum expliziten Aufrufen des Standard-Konstruktors, der aber in der 2. Version implizit auch aufgerufen werden müsste... WIESO

Felix
P.S: An void*: Hast du jetzt die Version von Fireflow benutzt?
-
-
Phoemuex schrieb:
Zwei Probleme gibt es aber immer noch:
- Der Destruktor muss auch in allen abgeleiteten Klassen konsequent protected gehalten werden.
das war, warum ich sagte, das es nicht funktioniert. ich verstehe die aufgabenstellung dahin, dass es durch ein bestimmtes design der basisklasse garantiert nicht möglich ist, irgendeine abgeleitete klasse als automatisches objekt zu instantiieren. und da hilft ein geschützter destruktor in der basisklasse eben nicht - ich hatte zwei denkbare andere auswege genannt.
- Folgender Code funktioniert komischerweise, da würde ich auch um Erklärung bitten:
int main(void) { Erbe testobjekt(); return(0); }alter fehler - das ist keine variablendefinition, sondern eine funktionsdeklaration

-
Hi!
int main() { Erbe *testzeiger = new Erbe(); //ok Erbe testobjekt(); // VC++ 2005 //warning C4930: 'Erbe testobjekt(void)': Funktion mit Prototyp wurde nicht aufgerufen //(war eine Variablendefinition gemeint?) return 0; }Zitat Phoemuex "Der Destruktor muss auch in allen abgeleiteten Klassen konsequent protected gehalten werden."
Das ist nur logisch!
Wenn man eine abgeleitete Klasse instantiert, werden (sofern nicht abstrakt) auch deren Basisklassen
angelegt. Sollen diese Basisklassen nun auf dem Heap angelegt werden, gilt das natürlich auch für die abgeleitete Klasse.
Man kann doch nicht ein automatisches Objekt instantieren wollen, das seinerseits Basisklassen instantiert
welche Heap-Speicherung fordern.Es ist klar möglich die Heap-Speicherung für ein Objekt zu erzwingen und darum ging es.
camper versteh ich nicht.
-
Helmut S. schrieb:
Zitat Phoemuex "Der Destruktor muss auch in allen abgeleiteten Klassen konsequent protected gehalten werden."
Das ist nur logisch!
richtig. und eben deshalb ist das keine lösung der aufgabe, so wie sie gestellt wurde. denn ob sich derjenige, der die abgeleitete klasse implementiert, an diese einschränkung hält, liegt aushalb der kontrolle desjenigen, der die klassen A und B baut.
original:
Gibt es eine Technik, die es mir erlaubt zu erzwingen, dass nur mit new erzeugte Objekte an die Hinzufügen-Funktion meiner Klasse A übergeben werden können und damit alle meine Zeiger auf dynamisch reservierten Speicher zeigen?
wenn die vorgestellte lösung die gesuchte antwort wäre - und vioelleicht ist es das ja auch, nur verstehe ich es nicht so - dann wäre die einfachere antwort: disziplin, jedes objekt grundsätzlich nur mit new anzulegen. wenn jedes objekt mit new angelegt wurde, erfüllt das eben die geforederte bedingung. "den destruktor in abgeleiteten kalssen konsequent protected halten" ist eine anforderung an die disziplin des benutzers und eben keine technik, die mich darin hindert, es nicht zu tun. es gibt keine möglichkeit, das ganze so zu schreiben, dass es beim compilieren oder wenigstens garantiert beim programablauf zu einem definierten fehler kommt, wenn man mal vergessen hat, sich an diese regel zu halten. eine technik, die das nicht erlaubt, halte ich für keine lösung des problems.
-
warning C4930: 'Erbe testobjekt(void)': Funktion mit Prototyp wurde nicht aufgerufen (war eine Variablendefinition gemeint?)
Ja, die Definition wird laut Standard angesehen als eine Funktionsdeklaration, die keinen Parameter entgegennimmt und ein 'Erbe' zurückgibt. Lokale Variablen definierst du mit "Erbe test;" oder "Erbe test = Erbe();".
-
Hallo,
P.S: An void*: Hast du jetzt die Version von Fireflow benutzt?
Ja, da es mir darauf ankam, dass der Benutzer der Klasse keine Objekte auf dem Stack erstellen kann.
Ich hatte wohl die Aufgabenstellung selbst nicht ganz zu Ende gedacht, aber Camper hat das ja erledigt.
Jetzt werde ich mich aber erst mal in die Beispiele nach meinem letzten Post vertiefen...das ist alles sehr interessant.Gruß
void*