2013-03-21 8 views
8

Wenn ich eine boost::shared_array<T> (oder eine boost::shared_ptr<T[]>) habe, gibt es eine Möglichkeit, eine boost::shared_ptr<T> zu erhalten, die mit dem Array teilt?Element von shared_array als shared_ptr?

So zum Beispiel, ich könnte schreiben wollen:

shared_array<int> array(new int[10]); 
shared_ptr<int> element = &array[2]; 

Ich weiß, dass ich nicht &array[2] verwenden kann, weil es nur int * geben, und es wäre gefährlich für shared_ptr<int> einen impliziten Konstruktor hat das wird diesen Typ annehmen. Im Idealfall eine Instanz-Methode auf sie, so etwas wie haben shared_array<int> würde:

shared_ptr<int> element = array.shared_ptr_to(2); 

Leider kann ich nichts dergleichen finden. Es gibt einen Aliasing-Konstruktor unter shared_ptr<int>, der mit einem anderen shared_ptr<T> aliasiert, aber kein Aliasing mit shared_array<T>; so kann ich dies nicht schreiben entweder (es wird nicht kompiliert):

shared_ptr<int> element(array, &array[2]); 
//Can't convert 'array' from shared_array<int> to shared_ptr<int> 

Eine weitere Option, die ich mit gespielt war std::shared_ptr<T> (std statt boost) zu verwenden. Die Spezialisierung für T[] ist nicht standardisiert, also habe ich darüber nachgedacht, das selbst zu definieren. Leider glaube ich nicht, dass dies tatsächlich so möglich ist, dass es die Interna des Alias-Konstruktors nicht unterbricht, da es versucht, meine std::shared_ptr<T[]> in einen eigenen implementierungsspezifischen Supertyp umzuwandeln, was nicht mehr möglich ist. (Meins erbt derzeit im Moment nur vom Boost.) Das Schöne an dieser Idee wäre gewesen, dass ich meine Instanz shared_ptr_to Methode implementieren könnte.

Hier ist eine andere Idee, mit der ich experimentiert habe, aber ich denke nicht, dass sie effizient genug ist, um als etwas akzeptiert zu werden, das wir potenziell in einem großen Projekt verwenden werden.

template<typename T> 
boost::shared_ptr<T> GetElementPtr(const boost::shared_array<T> &array, size_t index) { 
    //This deleter works by holding on to the underlying array until the deleter itself is deleted. 
    struct { 
     boost::shared_array<T> array; 
     void operator()(T *) {} //No action required here. 
    } deleter = { array }; 
    return shared_ptr<T>(&array[index], deleter); 
} 

Das nächste, was ich versuche, werde rüstet 1.53.0 Boost (wir haben derzeit nur 1.50.0), mit shared_ptr<T[]> statt shared_array<T>, und auch immer boost statt std mit (auch für Nicht-Arrays). Ich hoffe, dies wird dann arbeiten, aber ich habe keine Chance zu versuchen, es noch hatte:

shared_ptr<int[]> array(new int[10]); 
shared_ptr<int> element(array, &array[2]); 

Natürlich habe ich immer noch die Instanz-Methode Syntax bevorzugen würden, aber ich denke, ich bin kein Glück mit diesem (kurz Boost modifizieren):

shared_ptr<int> element = array.shared_ptr_to(2); 

Hat jemand sonst irgendwelche Ideen?

+2

Ihre Deleter-Idee ist * die * Weise, wie es gemacht werden sollte. Es ist nichts besonders ineffektiv (wenn Sie Zweifel haben, führen Sie ein Experiment mit einem Profiler durch). –

+0

@ n.m. Ich denke, es wird einen neuen Kontrollblock (auf dem Heap) für den neuen 'shared_ptr' erstellen, auf den verwiesen wird. Zugegeben, es ist nicht so schlimm, aber es ist immer noch besser, wenn es den gleichen Kontrollblock teilen kann. :) – entheh

+0

Brauchen Sie überhaupt ein 'shared_array'? Wenn Sie eine 'shared_ptr ' (mit entsprechenden Deleter) hatten, könnten Sie die Aliasing-Konstruktoren verwenden. –

Antwort

1

Sie tun seltsame Sachen. Warum brauchen Sie shared_ptr zu Element? Möchten Sie, dass das Element des Arrays irgendwo anders übergeben wird und halten Sie Ihr Array vor dem Entfernen?

Wenn ja, dann ist std::vector<shared_ptr<T>> dafür besser geeignet. Diese Lösung ist sicher, Standard und hat feine Granularität auf Objekte entfernen

+0

Ja, ich möchte das ganze Array behalten, solange ich einen Zeiger auf irgendein Element habe.Die Art des Szenarios, in dem dies geschieht, ist, wenn eine API zur Sicherheit ein shared_ptr verwendet, aber gelegentlich ordnen wir viele ähnliche Objekte auf einmal zu und möchten den Heap-Overhead minimieren. In der Zwischenzeit muss der Rest der Anwendung nicht unbedingt wissen, dass die Objekte aus einem Array stammen. - Obwohl ich denke, dass "weak_ptr" für diese Fälle besser geeignet ist, aber natürlich gelten alle oben genannten Fragen immer noch, aber mit leichten Verbesserungen, um an einigen Stellen "weak_ptr" anstelle von "shared_ptr" zu sagen. – entheh

+0

@entheh Und was ist mit Array Lebensdauer, wann würde es veröffentlicht werden? – kassak

+0

Welches Objekt das shared_ptr zu dem Array besitzt, wäre dafür verantwortlich. Aber zum Beispiel könnte ich mal schwache Elemente zu Elementen bringen und sie Lua zur Verfügung stellen, und es wird immer jemanden geben, der versehentlich ein Objekt in Lua zu lange hält. Wir wollen definiertes Verhalten, wenn das passiert, das ist alles. (Ich bin mir nicht sicher, ob der shared_ptr-to-element-Fall für uns aufkommt.) – entheh

0

boost::shared_ptr scheint nicht zu unterstützen, diese nativly. Vielleicht können Sie dies mit einem benutzerdefinierten Deleter umgehen.Aber std::shared_ptr bietet einen speziellen Konstruktor zu unterstützen, was Sie wollen:

struct foo 
{ 
    int a; 
    double b; 
}; 

int main() 
{ 
    auto sp1 = std::make_shared<foo>(); 
    std::shared_ptr<int> sp2 (sp1,&sp1->a); 
} 

Hier sp1 und sp2 Aktienbesitz des foo Objekt, sondern sp2 verweist auf ein Mitglied davon. Wenn sp1 zerstört wird, ist das Objekt foo noch am Leben und sp2 wird weiterhin gültig sein.

+1

Ja, das ist der Alias-Konstruktor (und Boost _does_ have it) :). Leider wird es nur eine 'shared_ptr ' nehmen, während ich es eine 'shared_array ' übergeben wollte. – entheh

+0

Es sieht so aus, als hätte Boost 1.53 und höher 'shared_ptr ' (siehe Ende der Einführung unter http://www.boost.org/doc/libs/1_54_0/libs/smart_ptr/shared_ptr.htm); Sie müssen jedoch möglicherweise anstelle von 'shared_array' diese verwenden, um diese Spezialisierung zu verwenden. –

0

Folgendes habe ich am Ende gemacht.

Ich habe meine eigene Implementierung von shared_array<T> gemacht. Es erweitert effektiv shared_ptr<vector<T>>, außer dass es tatsächlich meinen eigenen Wrapper für vector<T> erweitert, so dass der Benutzer den Vektor nicht erhalten kann. Dies bedeutet, dass ich garantieren kann, dass die Größe nicht geändert wird. Dann implementierte ich die Instanzmethoden, die ich benötigte - einschließlich weak_ptr_to(size_t) und natürlich operator[].

Meine Implementierung verwendet std::make_shared, um den Vektor zu erstellen. Daher ordnet der Vektor seinen internen Array-Speicher getrennt vom Steuerblock zu, aber der Vektor selbst wird ein Mitglied des Steuerblocks. Es entspricht also ungefähr dem Vergessen, std::make_shared für einen normalen Typ zu verwenden - aber da dies Arrays sind, sind sie wahrscheinlich groß und wenig, also ist es weniger wichtig.

Ich könnte auch eine Implementierung erstellen, die auf shared_ptr<T> basiert, aber mit default_delete<T[]> oder was auch immer erforderlich ist, aber es würde das Array separat vom Steuerblock zuordnen müssen (so gibt es nicht viel sparen gegenüber Vektor). Ich glaube nicht, dass es einen portablen Weg gibt, um ein dynamisch großes Array in den Kontrollblock einzubetten.

Oder meine Implementierung könnte auf boost::shared_array<T> basieren, und verwenden Sie die benutzerdefinierte Deleter bei der Übernahme von Element Zeigern (gemäß dem Beispiel in der Frage). Das ist wahrscheinlich in den meisten Fällen schlimmer, denn statt eines einmaligen Treffers, der das Array zuweist, erhalten wir jedes Mal einen Treffer, wenn wir einen Alias-Zeiger nehmen (was bei sehr kurzlebigen Pointern sehr häufig passieren kann).

Ich denke, der einzige vernünftige Weg, es noch optimaler zu bekommen, wäre, den neuesten Boost zu verwenden (wenn es funktioniert; ich habe es nicht versucht, bevor ich meine Meinung änderte, hauptsächlich wegen des Verlangens nach meine eigenen Instanzmitglieder). Und das bedeutet natürlich, dass man die boost überall benutzt, sogar für einzelne Objekte.

Aber, der Hauptvorteil mit dem, was mit Ich ging ist Debugger Visual Studio ist (ich sagte) gut an den Inhalt von std :: Anzeige shared_ptrs und std :: Vektoren und (wir erwarten) weniger gut bei der Analyse der Inhalte von Boost-Dingen oder benutzerdefinierten Dingen.

Also ich denke, was ich getan habe, ist im Grunde optimal. :)