(gelöst) [C++] Zwei Objekte in einem new

Programmiersprachen, APIs, Bibliotheken, Open Source Engines, Debugging, Quellcode Fehler und alles was mit praktischer Programmierung zu tun hat.
Antworten
Benutzeravatar
Krishty
Establishment
Beiträge: 8238
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

(gelöst) [C++] Zwei Objekte in einem new

Beitrag von Krishty »

Hi,

Ich habe zwei Klassen. Die erste Klasse wird bei jeder Konstruktion definitiv eine Instanz der zweiten Klasse allokieren. Ich möchte nun, dass beide Objekte in nur einem Speicherblock mit einer Allokation untergebracht werden können.

Normalerweise würde ich die zweite Klasse einfach zu einem Member der ersten Klasse machen, aber es handelt sich um ein Interface … alle Implementationen des Interfaces müssten identischer Größe sein, weil sie nur pur virtuelle Funktionen implementieren und nichts Neues (weder Variablen noch Funktionen) hinzufügen.

In VC6 konnte man afaik den operator sizeof überladen, so dass man sizeof(Klasse1) einfach um sizeof(Klasse2) Bytes größer gemacht und das zusätzliche Objekt per (this + 1) erhalten hat … afaik ist das Überladen von sizeof aber nicht mehr möglich …

… kann ich den operator new der Klasse so überladen, dass er sizeof(Klasse2) (plus Alignment) Bytes mehr allokiert?

Gruß, Ky
Zuletzt geändert von Krishty am 04.06.2009, 16:05, insgesamt 1-mal geändert.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
TGGC
Establishment
Beiträge: 569
Registriert: 15.05.2009, 18:14
Benutzertext: Ich _bin_ es.
Alter Benutzername: TGGC
Echter Name: Ich _bin_ es.
Wohnort: Mainz
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von TGGC »

Sowas in der Art willst du aber nicht?

Code: Alles auswählen

BYTE* Speicher= new BYTE[ Bla1 + Bla2];
Klasse2 *p1 = new (Speicher) Klasse1();
Klasse2 *p2 = new (Speicher + Bla1) Klasse2();
Nur so als Idee... f'`8k

[ ] Autocogito


Gruß, TGGC (Was Gamestar sagt...)
knivil
Beiträge: 14
Registriert: 03.04.2008, 01:03

Re: [C++] Zwei Objekte in einem new

Beitrag von knivil »

Sowas in der Art willst du aber nicht?
Sieht so aus. Die Frage bleibt: Warum?
Benutzeravatar
Krishty
Establishment
Beiträge: 8238
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Krishty »

So in etwa, nur dass das erste Objekt allein den Speicher des zweiten Objekts verwalten soll (ohne dass es nach außen sichtbar ist) ... und Stack-tauglich sollte es auch sein ... mit sizeof ging das damals, aber jetzt ... :(
knivil hat geschrieben:Sieht so aus. Die Frage bleibt: Warum?
Ist mir ein Dorn im Auge, wenn ein Objekt quasi nur ein Zeiger ist und zusätzlich Speicher allokiert, wenn doch von vornherein feststeht, wie groß die Daten mal sein werden - bloß um der virtuellen Funktionen Willen ... außerdem mag ich die Sachen auf dem Stack lieber als auf dem Heap.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
TGGC
Establishment
Beiträge: 569
Registriert: 15.05.2009, 18:14
Benutzertext: Ich _bin_ es.
Alter Benutzername: TGGC
Echter Name: Ich _bin_ es.
Wohnort: Mainz
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von TGGC »

Wo der Speicher herkommt, ist damit ja auch nicht festgelegt, "Speicher" koennte genauso auf ein Stueck Speicher auf dem Stack zeigen. Ansonsten sorge halt einfach dafuer das deine Klasse1 um n Bytes zu gross ist, indem du entsprechende Member hinzufuegst - da dein n ja ehh konstant sein soll. f'`8k

[ ] Autocogito


Gruß, TGGC (Was Gamestar sagt...)
Benutzeravatar
Schrompf
Moderator
Beiträge: 4854
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Schrompf »

Das wäre auch mein Vorschlag: verschaffe Dir zusätzlichen Platz in Klasse1, indem Du z.B. ein char[20] mit reinnimmst. Dann benutze Placement New auf diesem Zeiger, wie TGGC das vorgeschlagen hat.

Wirst Du denn wirklich viele hunderttausend Instanzen der Klasse anlegen, damit sich das Manöver lohnt? Dadurch verbaust Du Dir nämlich auch die Möglichkeit, eine Instanz der zweiten Klasse nachträglich reinzureichen. Du bist damit quasi gezwungen, die zweite Klasse über eine Factory im Konstruktor der ersten Klasse anzulegen.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Aramis »

In VC6 konnte man afaik den operator sizeof überladen, so dass man sizeof(Klasse1) einfach um sizeof(Klasse2) Bytes größer gemacht und das zusätzliche Objekt per (this + 1) erhalten hat … afaik ist das Überladen von sizeof aber nicht mehr möglich …
Exakt, und zwar weil sizeof *zwingend* eine Konstante sein muss. Unter anderem, weil es sonst nicht in Templates benützt werden könnte. Mit constexpr im neuen Sprachstandard wäre es zwar theoretisch möglich den Operator überladbar zu machen, glücklicherweise scheint es aber niemand vorzuhaben.
Normalerweise würde ich die zweite Klasse einfach zu einem Member der ersten Klasse machen, aber es handelt sich um ein Interface … alle Implementationen des Interfaces müssten identischer Größe sein, weil sie nur pur virtuelle Funktionen implementieren und nichts Neues (weder Variablen noch Funktionen) hinzufügen.
Don't do it, it's evil. Soweit ich weiß, sagt der Standard nichts über die tatsächliche Implementierung virtueller Funktionen aus.
Ist mir ein Dorn im Auge, wenn ein Objekt quasi nur ein Zeiger ist und zusätzlich Speicher allokiert, wenn doch von vornherein feststeht, wie groß die Daten mal sein werden - bloß um der virtuellen Funktionen Willen
Dann solltest du nicht am Objekt-Design ansetzen, sondern am Speichermanager. Beispielsweise könntest du für stark frequentierte Klassen, wie diese, einfach einen separaten Heap einsetzen, den du manuell verwaltest und deinen Bedürfnissen anpasst. Ich bezweifle aber dass es etwas bringen wird. Bei Assimp haben wir etwas ähnliches probiert (aber schlussendlich dann doch gelassen) um die vielen Mini-allocs für die einzelnen Polygone zu beschleunigen.

Alex
Jörg
Establishment
Beiträge: 296
Registriert: 03.12.2005, 13:06
Wohnort: Trondheim
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Jörg »

Wie dynamisch muss die Loesung sein? Ist die Beziehung zwischen Klasse und Interface-Implementierung fest fuer die Lebenszeit? Wer garantiert, dass deine Interfaces wirklich alle immer in das gleiche Schema (Aufbau) passen....neben der vtable wird doch irgendwann noch etwas dazu kommen an Daten, oder? Was spricht gegen eine feste Beziehung, die sich ueber Vererbung mit Template-Unterstuetzung leicht erledigen laesst ? Fuer den Fall, dass das Interface (seine Implementierung meine ich) mal ausgetauscht werden muss, tut es eventuell ein angepasster copy-constructor auch (je nach Haeufigkeit).
Benutzeravatar
Aramis
Moderator
Beiträge: 1458
Registriert: 25.02.2009, 19:50
Echter Name: Alexander Gessler
Wohnort: 2016
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Aramis »

Das folgende kompiliert zumindest mal unter MSVC. Ob es wirklich standardkonform ist - eher nicht. Aber vielleicht ist es ein Ansatz, was nicht heißt, dass ich das ganze befürworte ;-)

Code: Alles auswählen


// --------------------------------------------------------------------------------------------
template <typename T>
struct OnStack {
	enum {extra_size=0};
	static OnStack Get () {return OnStack();}
};

template <typename T>
void* operator new(size_t s, const OnStack<T>&) { 
	return alloca(s+ OnStack<T> :: extra_size ); // afaik manchmal auch als _alloca, vor allem (afaik) nicht-standard!
}

template <typename T>
struct OnHeap {
	enum {extra_size=0};
};

template <typename T>
void* operator new(size_t s, const OnHeap<T>&) { 
	return malloc(s+ OnHeap<T> :: extra_size );
}

// --------------------------------------------------------------------------------------------
#define IMPLEMENT_OVERRIDE_SIZEOF(cl) \
template <> \
struct OnStack<foobar> { \
	enum {extra_size=cl :: extra_size}; \
	static OnStack Get () {return OnStack();} \
}; \
template <> \
struct OnHeap<foobar> { \
	enum {extra_size=cl :: extra_size}; \
	static OnHeap Get () {return OnHeap();} \
};

#define OVERRIDE_SIZEOF(extra) \
	enum {extra_size=extra};

// --------------------------------------------------------------------------------------------
#define NEW_HEAP(alloctype) \
	new (OnHeap<alloctype> :: Get()) alloctype;

#define NEW_STACK(alloctype) \
	new (OnStack<alloctype> :: Get()) alloctype;

// --------------------------------------------------------------------------------------------
class foobar	{
public:
	OVERRIDE_SIZEOF(10);
};
IMPLEMENT_OVERRIDE_SIZEOF(foobar);

// --------------------------------------------------------------------------------------------
void test
{
	int* p1 = NEW_STACK(int);
	foobar* p2 = NEW_STACK(foobar);
	foobar* p3 = NEW_HEAP(foobar);
}

Benutzeravatar
Krishty
Establishment
Beiträge: 8238
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Krishty »

@TGGC, Schrompf: Placement-new scheint die optimale Lösung zu sein … bevor ich es aber implementiere, noch eine Frage: Gibt es ein entsprechendes delete (ich habe noch im Kopf "There is no placement delete in C++"), oder muss ich der placement-allokierten Klasse eine Funktion zur Bereinigung mitgeben, wenn ich die Daten wieder zerstören will?
Schrompf hat geschrieben:Wirst Du denn wirklich viele hunderttausend Instanzen der Klasse anlegen, damit sich das Manöver lohnt? Dadurch verbaust Du Dir nämlich auch die Möglichkeit, eine Instanz der zweiten Klasse nachträglich reinzureichen. Du bist damit quasi gezwungen, die zweite Klasse über eine Factory im Konstruktor der ersten Klasse anzulegen.
Nicht hunderttausende, aber tausende schon. Ich muss sowieso den Weg über die Factory gehen, da die Schnittstelle pur virtuelle Funktionen enthält und nach außen unsichtbar ist. Entsprechend ist in der Oberklasse auch schon alles bezüglich RAII und der Erkennung ungültiger Zustände abgeklärt.
Aramis hat geschrieben:Exakt, und zwar weil sizeof *zwingend* eine Konstante sein muss. Unter anderem, weil es sonst nicht in Templates benützt werden könnte.
Nicht, dass du mich falsch verstehst: Auch der Wert des überladenen operator sizeof in VC6 musste eine zur Kompilierzeit bekannte Konstante sein, auch wenn dahinter kein Sprachkonzept wie constexpr stand. Hätte man sizeof laufzeitabhängig machen können, wäre da sicher das ein oder andere Massaker rausgekommen :D
Aramis hat geschrieben:Don't do it, it's evil. Soweit ich weiß, sagt der Standard nichts über die tatsächliche Implementierung virtueller Funktionen aus.
Aber ganz egal, wie (oder ob überhaupt) die virtuellen Funktionen implementiert werden – sizeof dürfte einen da nicht betrügen.
Aramis hat geschrieben:Dann solltest du nicht am Objekt-Design ansetzen, sondern am Speichermanager.
Ich benutze schon den Low-Fragmentation-Heap … es geht auch weniger darum, noch 0.0001% Performance rauszuholen, sondern um Kosmetik und Perfektion. So lang wie du mich kennst, müsstest du wissen, dass ich auch die absurdesten Experimente nicht aufgebe, bis mir alles um die Ohren fliegt ;)

Deinen Code schaue ich mir gleich an, habe nun zu lange an diesem Post getippt.
Jörg hat geschrieben:Wie dynamisch muss die Loesung sein? Ist die Beziehung zwischen Klasse und Interface-Implementierung fest fuer die Lebenszeit? Wer garantiert, dass deine Interfaces wirklich alle immer in das gleiche Schema (Aufbau) passen....neben der vtable wird doch irgendwann noch etwas dazu kommen an Daten, oder? Was spricht gegen eine feste Beziehung, die sich ueber Vererbung mit Template-Unterstuetzung leicht erledigen laesst ? Fuer den Fall, dass das Interface (seine Implementierung meine ich) mal ausgetauscht werden muss, tut es eventuell ein angepasster copy-constructor auch (je nach Haeufigkeit).
Die Beziehung ist nicht fest auf Lebenszeit. Dass alle Implementationen der Schnittstelle nahezu gleich sind, wird dadurch garantiert, dass sie alle nur Instanzierungen desselben Templates mit je anderem enum-Parameter und ohne Spezialisierung (d.h. alle vom Compiler generiert) sind. Desweiteren verwaltet die Klasse das Interface und lässt so nichts „Fremdes“ zu.
Daten kommen auch nicht mehr hinzu. Ich brauche einfach nur andere Ergebnisse in den Funktionen (und auch die sind komplett Compiler-generiert), alle Daten sind bereits in der Basisklasse gespeichert und werden es wohl auch immer sein. Eine feste Beziehung mit Templates hatte ich bis letzten Monat implementiert, deswegen ist der Code aber am Ende exponentiell gewachsen. Für einen Copy-Ctor sind die Daten, die die Implementierungen der Interfaces frei allokiert mitbringen, einfach zu groß und zu sperrig (~4 MiB pro Kopie).


Achja, danke für die rege Teilnahme – sonst dümpeln meine Threads ja tagelang vor sich hin, bevor sich was tut :D
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Schrompf
Moderator
Beiträge: 4854
Registriert: 25.02.2009, 23:44
Benutzertext: Lernt nur selten dazu
Echter Name: Thomas Ziegenhagen
Wohnort: Dresden
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Schrompf »

Krishty hat geschrieben:@TGGC, Schrompf: Placement-new scheint die optimale Lösung zu sein … bevor ich es aber implementiere, noch eine Frage: Gibt es ein entsprechendes delete (ich habe noch im Kopf "There is no placement delete in C++"), oder muss ich der placement-allokierten Klasse eine Funktion zur Bereinigung mitgeben, wenn ich die Daten wieder zerstören will?
Björne schreibt das gleiche: http://www.research.att.com/~bs/bs_faq2 ... ent-delete

Essenz: Es gibt kein "Placement Delete", aber wohl der manuelle Aufruf des Destruktors. Das kommt dem "Placement Delete" so nahe, wie ich mir nur vorstellen kann.

Code: Alles auswählen

Klasse a;
a.~Klasse();
Ich fänd's schön, wenn man auch Konstruktoren auf diese Art manuell aufrufen könnte. Wäre nützlich zur Umleitung in der Implementation von Konstruktoren.
Achja, danke für die rege Teilnahme - sonst dümpeln meine Threads ja tagelang vor sich hin, bevor sich was tut :D
Weil Du was Entsetzliches vorhast, zu dem jeder ne Meinung hat :-)
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
Benutzeravatar
Krishty
Establishment
Beiträge: 8238
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Krishty »

Schrompf hat geschrieben:Ich fänd's schön, wenn man auch Konstruktoren auf diese Art manuell aufrufen könnte.
Bei VC6 konnte man das, darum habe ich auch mit dem explicit-destructor-call so gehadert – ich dachte, das wäre auch so eine Microsoft-Extension von früher … aber gut, dann baue ich mal einen Prototyp :)
Schrompf hat geschrieben:Weil Du was Entsetzliches vorhast
Als ob das zum ersten Mal so wäre :D
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
TGGC
Establishment
Beiträge: 569
Registriert: 15.05.2009, 18:14
Benutzertext: Ich _bin_ es.
Alter Benutzername: TGGC
Echter Name: Ich _bin_ es.
Wohnort: Mainz
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von TGGC »

Krishty hat geschrieben:@TGGC, Schrompf: Placement-new scheint die optimale Lösung zu sein … bevor ich es aber implementiere, noch eine Frage: Gibt es ein entsprechendes delete (ich habe noch im Kopf "There is no placement delete in C++"), oder muss ich der placement-allokierten Klasse eine Funktion zur Bereinigung mitgeben, wenn ich die Daten wieder zerstören will?
Nennen wir diese Funktion doch einen "Destruktor". f'`8k

[ ] Autocogito


Gruß, TGGC (Was Gamestar sagt...)
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von kimmi »

Hast du dir auch schon mal den Small-Object-Allocator der Loki-Lib angesehen? Viellleicht bietet der dir etwas, der deinen Anforderungen entgegenkommt.

Gruß Kimmi
Benutzeravatar
Krishty
Establishment
Beiträge: 8238
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Krishty »

Ja … das Buch liegt eigentlich nie weiter als zwei Meter entfernt … aber die besten Allokationen sind eben die, die man garnicht erst durchführt.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
Establishment
Beiträge: 8238
Registriert: 26.02.2009, 11:18
Benutzertext: state is the enemy
Kontaktdaten:

Re: [C++] Zwei Objekte in einem new

Beitrag von Krishty »

Okay, es scheint zu funktionieren – keine Memory Leaks, keine Zugriffsverletzungen beim Aufruf der virtuellen Funktionen, alles wie gehabt :)

Nochmal für’s Protokoll:
– Ausreichend Speicher in der Containerklasse reservieren.
– Objekt mit Placement-New füllen.
– Objekt mit explizitem Destruktoraufruf wieder freigeben.

Ein Beispiel, der Übersicht halber ohne virtuelle Funktionen:

Code: Alles auswählen

class Container {
    class Subclass { … };

    char Pool[sizeof(Subclass)];

    Container() {
        new (this->Pool) Subclass;
    }

    ~Container() {
        reinterpret_cast<Subclass *>(this->Pool)->~Subclass();
    }
};
Ich danke euch für eure Hilfe!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Antworten