Wie könnte man new und delete auf einen Klassenzeiger verbieten
-
Hallo zusammen,
- Klassenhirarchie mit der abstrakten Klasse Root als Wurzelklasse.
- Alle Instanzen der abgeleiteten Klassen werden in ObjDB gespeichert.
- ObjDB ruft beim Start der Applikation die Funktion CreateObjects() auf
- Innerhalb der Funktion CreateObjects() werden alle Objekte erzeugt.
- ObjDB löscht beim beenden der Applikation alle ObjekteDas ganze stellt ein Framework für Maschinensteuerungen dar. Da sind die realen Objekte auf der Maschine festgeschraubt, es macht also keinen Sinn während der Laufzeit des Programms Objekte hinzuzufügen oder zu zerstören.
Zum erzeugen der Instanzen habe ich das folgende Template:
template<typename T> T* CreateObj () { T* obj = new T(); if (obj != NULL) gObjDB.AddObject (obj); return obj; }und zum abholen eines Pointers auf eine Instanz:
template<typename T> T* Object (const Tstring& name) { VRoot* r = gObjDB.Object (name); ASSERT (r != NULL); if (r != NULL) { ASSERT (dynamic_cast<T*>(r)); if (!dynamic_cast<T*>(r)) r = NULL; } return static_cast<T*> (r); }Die Gefahr besteht, dass jemand auf die Idee kommt, eine Klasse welche in der Hirarchie von Root ist, anstatt mit CreateObject<classType> () mit new zu erzeugen.
Die Gefahr besteht, dass jemand auf die Idee kommt, eine Instanz welche mit Object<classType> (className) geholt wird, mit delete zu löschen.
Beispiel:
Root<---Model<---ProbeProbe* p = new Probe(); // sollte Fehler beim kompilieren erzeugen Probe* p = Object<Probe>("Probe"); delete p; // sollte Fehler beim kompilieren erzeugenKann man sowas irgendwie machen, oder muss ich mich nach wie vor auf die Disziplin meiner Kollegen verlassen?
Herzliche Grüsse
Walter
-
Eine Moeglichkeit waere, den Konstruktor und Destruktor private zu machen. Ich habs selber noch nie ausprobiert aber das muesste klappen.
-
Du könntest std::shared_ptr verwenden. (Um das delete zu verhindern)
Warum willst du new verbieten? Wenn es sein muss, könntest du dir eine Klasse schreiben, die den Pointer verwaltet und die * und -> operatoren überläd. Diese Klasse könntest du dann zurückgeben. Um new zu verbieten musst du dann die Konstruktoren & Destruktoren dieser Klasse private machen, und CreateObject() wird ein friend.
-
Wenn du nur new und delete verbieten willst, kannst du sie auch für die Klasse überladen und private machen. Konstruktor und Destruktor private zu machen ist eleganter, erlaubt dir aber nicht mehr, Stack-Objekte zu erstellen (außer in friend-Funktionen und der eigenen Klasse). Wenn du nur new und delete verbietest, dann kann das Designfehler verhindern, wenn jemand aber ganz hartnäckig ist, könntest du im Code Lösungen mit malloc() und placement new finden...
-
Außerdem könnte jemand etwas in der Art machen:
Object1 myObj; gObjDB.addObject (&myObj);Das gibt bestimmt einen Segfault oder?
-
Hallo zusammen,
@icarus2
Das habe ich mal ausprobiert. Aber da sind genau die Nachteile die wxSkip aufgeführt hat das NoGo.Warum willst du new verbieten?
weil eine mit new erstellte Instanz nicht automatisch in ObjDB gespeichert wird. Da die Funktionen der Application Klasse aber ausschliesslich mit den Instanzen in ObjDB arbeiten sehe ich es schon kommen, dass einer fragt "warum wird bei meiner neuen Klasse zB. OnLoadSetup() nicht aufgerufen?"
Du könntest std::shared_ptr verwenden. (Um das delete zu verhindern)
Ich hätte lieber einen Kompilierfehler.
Wenn es sein muss, könntest du dir eine Klasse schreiben, die den Pointer verwaltet und die * und -> operatoren überläd. Diese Klasse könntest du dann zurückgeben. Um new zu verbieten musst du dann die Konstruktoren & Destruktoren dieser Klasse private machen, und CreateObject() wird ein friend.
Das ist eigentlich genau das was ich suche. Schön wäre es wenn man diese Funktionalität gleich der Root Klasse geben könnte, dann wären die Änderungen an der Bibliothek marginal

C/C++ Code:
Object1 myObj;
gObjDB.addObject (&myObj);Oops, daran habe ich gar nicht gedacht. Ich hab's mal ausprobiert, gibt eine Access Violation. Dieser Fehler wird (wenn nicht absichtlich eingebaut) schwer zu finden sein. Das kann ich aber mit dem Verstecken der ObjDB beheben.
wenn jemand aber ganz hartnäckig ist, könntest du im Code Lösungen mit malloc() und placement new finden...
frei nach Scott Meyers "Mach es schwierig eine Schnittstelle falsch zu benutzen". "Holzhammermethoden" kann man IMHO nicht ausschliessen.
Durch das (durch)Lesen von Scott Meyers "Effektiv C++ programmieren (3. Auflage)" bin ich überhaupt erst auf die Idee gekommen, dass an meiner Bibliothek noch einiges zu tun ist. Aber es Hilft wohl nichts, ich muss wahrscheinlich das ganze Buch durcharbeiten ausser jemand erbarmt sich und nennt mir die Tipps die mir bei der Lösung helfen könnten.
Herzliche Grüsse
Walter
-
Fassen wir zusammen:
Anzahl und Typ der Objekte ist zur Compiletime bekannt.
Die Objekte sollen die selbe Lebensdauer haben wie das ObjDB Objekt.Dann frag ich mich jetzt, wieso die Objekte nicht einfach Member von ObjDB sein können!?
-
weicher schrieb:
Warum willst du new verbieten?
weil eine mit new erstellte Instanz nicht automatisch in ObjDB gespeichert wird.
Warum nicht? Sowas sollte nach Möglichkeit in den Konstruktor der Basisklasse.
Das ist eigentlich genau das was ich suche.
Findest du? Klingt für mich nach einer sehr umständlichen Lösung. Es reicht völlig aus, new und delete für deine Basisklasse private zu machen und CreateObj zum friend. Das Umgehen mit Placement New ist dann auch nicht möglich, weil ein operator new reicht, dass der Compiler nicht mehr die globalen op new in Betracht zieht - also auch kein Placement New.
Aber es Hilft wohl nichts, ich muss wahrscheinlich das ganze Buch durcharbeiten ausser jemand erbarmt sich und nennt mir die Tipps die mir bei der Lösung helfen könnten.
Selbst wenn sich jemand erbarmt, kommst du um das Buch nicht herum. Das Buch (und andere), bzw. der Inhalt sind für ein gutes C++ Sprachverständnis ein absolutes Muss.
-
Das ganze stellt ein Framework für Maschinensteuerungen dar. Da sind die realen Objekte auf der Maschine festgeschraubt, es macht also keinen Sinn während der Laufzeit des Programms Objekte hinzuzufügen oder zu zerstören.
Dafuer gibt es viele Moeglichkeiten. Eine beispielsweise ist
static. Klassen, die Arbeit an das eigentliche "Device" delegieren, eine andere.Kann man sowas irgendwie machen, oder muss ich mich nach wie vor auf die Disziplin meiner Kollegen verlassen?
C++ is not a security device. Und warum sollte man sich nicht auf die Kollegen verlassen, wenn dieses Verhalten explizit dokumentiert und nachschlagbar ist?
-
weicher schrieb:
- Alle Instanzen der abgeleiteten Klassen werden in ObjDB gespeichert.
[...]
- ObjDB löscht beim beenden der Applikation alle ObjekteDie beiden Anforderungen implizieren, dass Stack-Objekte nicht erlaubt sind. Die Einwände gegen private Konstruktoren und Destruktoren fallen damit weg, wenn ich das richtig sehe.
Das ganze stellt ein Framework für Maschinensteuerungen dar. Da sind die realen Objekte auf der Maschine festgeschraubt, es macht also keinen Sinn während der Laufzeit des Programms Objekte hinzuzufügen oder zu zerstören.
Die Argumentation verstehe ich nicht. Kann die Maschine nicht umkonfiguriert werden? So dass zum Beispiel die Objekte nach Programmstart -- also zur Laufzeit -- nach den Einträgen in einer Konfigurationsdatei o.ä. erzeugt werden.
knivil schrieb:
Und warum sollte man sich nicht auf die Kollegen verlassen, wenn dieses Verhalten explizit dokumentiert und nachschlagbar ist?
Weil Kollegen Schweinehunde sind
Wenn da erstmal einer drei Wochen lang irgendwas gefrickelt hat und alles funktioniert, wirst du wohl keinen Projektleiter finden, der auf deinen Einwand hin eine riskante Umstrukturierung beauftragt. Schau dir Java an, welches dieses Prinzip perfektioniert hat. Eine solche Sprache ist genau deshalb für große Projekte mit vielen Mitarbeitern unterschiedlicher Qualifikationslevel geeignet, weil sie automatisch gewisse Standards erzwingt, ohne auf die Disziplin der Projektteilnehmer angewiesen zu sein.C++ is not a security device.
Wo siehst du hier einen Zusammenhang zu Security? Hanlon's Razor!
-
Hallo dot,
Fassen wir zusammen:
Anzahl und Typ der Objekte ist bekannt und zur Laufzeit konstant.
Die Objekte sollen die selbe Lebensdauer haben wie das ObjDB Objekt.Das stimmt! Aber nur genau für einen Maschinentyp.
Sinn der Bibliothek ist es aber, alle möglichen Maschinentypen möglichst Komfortabel aufbauen zu können und nicht immer wieder bei null zu beginnen.
Mit meiner Bibliothek kann man mit einem Wizard das Grundgerüst für eine neue Maschine erstellen. Die so erstellte Applikation hat bereits die Funktionen welche jede Maschine haben muss (Create, Init, Config; Setup, Manual, Automat).
Die Bibliothek stellt auch Klassen für häufig benötigte Elemente zur Verfügung. Diese kann man im Eventhandler CreateObjects () mit CreateObj<classType>() erzeugen ("Probe", "XY-Table", "Laser", "MeasSystem" usw.).
Herzliche Grüsse
Walter
-
Wäre es nicht vielleicht sinnvoller, jeden Maschinentyp auch durch einen eigenen Typ im Programm zu repräsentieren?
Schau dir vielleicht auch mal das Builder-Pattern an.
-
pumuckl schrieb:
Das Umgehen mit Placement New ist dann auch nicht möglich, weil ein operator new reicht, dass der Compiler nicht mehr die globalen op new in Betracht zieht - also auch kein Placement New.
Bei mir geht das.
class Test { void *operator new(size_t size) { return ::operator new(size); } void operator delete(void *ptr) { ::operator delete(ptr); } public: }; int main() { Test *c = static_cast<Test*>(malloc(sizeof(Test))); c = ::new(c) Test; c->~Test(); free(c); }
-
Hallo zusammen,
die Bibliothek ist ziemlich umfangreich, es ist schlicht illusorisch das ganze Konzept über den Haufen zu werfen da:
- die Bibliothek sehr einfach zu benutzen ist.
- viele Maschinen bereits Mithilfe dieser Bibliothek erstellt wurden
- fast alle Konzepte von Scott Meyers erfüllt sind. Ich hatte beim lesen fast bei jedem Tipp ein Deja Vu
- das kein Mensch bezahlen kann und will.Es geht wirklich nur darum meine Objekt Pointer sicher zu machen.
Der Aufwand muss sich aber in Grenzen halten da ich das aus finanziellen Gründen komplett in meiner Freizeit machen muss.
Ich glaube ich schau mir mal die Tipps 17, 49..52 nochmal genauer an, eventuell könnten die weiterhelfen.
Herzliche Grüsse
Walter
-
wxSkip schrieb:
Bei mir geht das.
Mit explizit qualifiziertem globalem new, ja. Aber irgendwo ist dann auch wirklich mal die Grenze zum mutwillig destruktiven Verhalten überschritten - Scheiße bauen kann man immer, z.B. einfach memset auf ein beliebiges Objekt

-
pumuckl schrieb:
wxSkip schrieb:
Bei mir geht das.
Mit explizit qualifiziertem globalem new, ja. Aber irgendwo ist dann auch wirklich mal die Grenze zum mutwillig destruktiven Verhalten überschritten - Scheiße bauen kann man immer, z.B. einfach memset auf ein beliebiges Objekt

Zumindest in generischem Code wäre das durchaus normal. Schließlich kann man sich kaum darauf verlassen, dass bei überladenem new-Operator auch an placement-new gedacht wurde. Beispielsweise ist das Verhalten der construct-Funktion des Standard-Allokator auch mit
template <class U, class... Args> void construct(U* p, Args&&... args); 12 Effects: ::new((void *)p) U(std::forward<Args>(args)...)beschrieben.
-
Wie wäre es mit:
class SaveRootPtr { private: Root *ptr; SaveRootPtr(Root *r): ptr(r) {} ~SaveRootPtr() {} public: // Operator -> und * überladen (habe gerade keine Ahnung wie das geht xD) //... template <class ObjectType> friend SaveRootPtr CreateObject(); };Und in createObject dann:
Root *r=new T(); return SaveRootPtr(r);Die Methoden der ObjDB Klasse bekommen dann ein SaveRootPtr als Argument.
So kann niemand Objekte ohne die createObject Methode erstellen.
-
Ich hab' jetzt nicht alle Antworten gelesen, aber...
Was spricht nun wirklich dagegen die Konstruktoren und Destruktoren private zu machen?
Die Factory-Funktion muss irgendwie Zugriff auf Konstruktor und Destruktor haben, aber das lässt sich ja einrichten (z.B. mittels "friend").Wenn alle Objekte sowieso zu einem bestimmten Zeitpunkt zerstört werden sollen, und keine Shared-Ownership nötig ist, dann kann man weiterhin einfach mit rohen Zeigern arbeiten.
new/delete funktionieren dann nimmer, und wenn keine Notwendigkeit besteht manuell new oder delete zu machen, und auch keine das "automatische delete" bei Programmende irgendwie verzögern zu können... dann sollte das doch damit gegessen sein. Oder übersehe ich was?p.S.: Die Konstruktoren/Destruktoren der Basisklassen müssen natürlich "protected" gemacht werden statt private. Oder aber die Basisklasse müsste alle abgeleiteten Klassen "kennen", was ... naja etwas komisch wäre.
-
Hallo hustbaer,
Was spricht nun wirklich dagegen die Konstruktoren und Destruktoren private zu machen?
Wie Du im PS sagts, geht's nur wenn die ctors und dtors in den Basisklassen nicht privat sondern protected sind.
Das Bedeutet, die neue Regel lautet "bei allen Instanzierbaren Klassen müssen ctor und dtor Privat sein". Damit habe ich nur die alte Regel "Objekte dürfen nur mit CreateObj() erzeugt und nie mit delete gelöscht werden" mit einer neuen Regel ausgetauscht.
Rein Gefühlsmässig ist der Verstoss gegen die neue Regel IMHO wahrscheinlicher

Herzliche Grüsse
Walter
-
Wenn Du du eine Bibliothek nur für Deppen programmierst, mußt Du Dich nicht wundern, wer sie später nur benutzt.
-
weicher schrieb:
Hallo hustbaer,
Was spricht nun wirklich dagegen die Konstruktoren und Destruktoren private zu machen?
Wie Du im PS sagts, geht's nur wenn die ctors und dtors in den Basisklassen nicht privat sondern protected sind.
Das Bedeutet, die neue Regel lautet "bei allen Instanzierbaren Klassen müssen ctor und dtor Privat sein". Damit habe ich nur die alte Regel "Objekte dürfen nur mit CreateObj() erzeugt und nie mit delete gelöscht werden" mit einer neuen Regel ausgetauscht.
Rein Gefühlsmässig ist der Verstoss gegen die neue Regel IMHO wahrscheinlicher

Herzliche Grüsse
WalterWas willst du jetzt eigentlich damit sagen?
Soll der Benutzer Stack-Objekte erstellen können? Wenn ja, was spricht dann gegen ein privates new und delete?EDIT: Meinst du damit, deine Kollegen würden sich nicht daran halten, Konstruktoren/Destruktoren bei neuen Klassen private/protected zu machen? Das kann man aber relativ einfach nachkorrigieren.