2016-03-23 21 views
3

Ich möchte sicherstellen, dass nur intelligente Zeiger aus meinen Klassen erstellt werden, so dass ich alle Konstruktoren geschützt habe.Stellen Sie sicher, dass nur Smartpointers erstellt werden

Um Objekte zu erstellen i bauen dieses „Unbebaubares“ -Politik:

template <typename T> 
class Buildable 
{ 
public: 
    template<typename ...Args> 
    static QSharedPointer<T> buildObject(Args&&... all) 
{ 
     return QSharedPointer<T>(new T(std::forward<Args>(all)...)); 
} 

}; 

Wenn ich das diese Politik in einer Basisklasse zu verwenden, alles gut geht. Aber wenn ich es in der Basis verwenden und eine abgeleitete Klasse wie folgt aus:

class A : public Buildable<A> {} 
class B : A, public Buildable<B>{} 

die Compiler argumentiert: Fehler: Mitglied ‚buildObject‘ gefunden in mehreren Basisklassen verschiedenen Typen

Ich wissen nicht, wie um dieses Problem zu lösen. Irgendwelche Ideen?

+0

Wenn Ihr Konstruktor ist 'private',' Klasse B: A, ..' kann nicht kompilieren 'A' zu konstruieren. Wenn es geschützt ist, braucht 'B' nicht länger smartPointer zu benutzen, und so ist A (geerbt) nicht in einem smartPointer ... – Jarod42

+0

Gibt es irgendeine Chance, etwas zu bauen, was ich will? um die Verwendung intelligenter Zeiger zu erzwingen? –

Antwort

3

Ursache des Fehlers

Dies liegt daran, die Mehrfachvererbung hier:

class B : A, public Buildable<B>{}; 

Es verursacht Klasse B von Buildable<A> erben und von Buildable<B>, die sowohl eine buildObject() Überlastung enthalten. Leider unterscheiden sich beide Überladungen nur durch den Rückgabetyp. Das ist nicht erlaubt.

Design Ausgabe

, können Sie dies leider nicht vermeiden können, wenn Sie einen zusätzlichen Parameter zu buildObject() schmuggeln könnte, was den Compiler richtigen Typ Ableitung zu verwenden erlauben könnte Mehrdeutigkeit aufzulösen zu vermeiden.

Aber haben Sie wirklich vor, hier mehrfache Vererbung in Ihrem Design zu haben? Wenn Ihre Klassen polymorph sein würden, könnte man nicht eine Klasse B verwenden wie folgt definiert:

class B : public A {}; // inherits from Buildable A 
... 
QSharedPointer<A> b = B::buildObject(); // 

Alternative Design

Die Alternative könnte sein, die Bauklasse der Ableitung am Boden zu setzen, um die Konflikte zu vermeiden. Machen Sie Ihre Konstrukteure für die Klassen A und B geschützt, und verwenden Sie diese Vorlage Klasse:

// (I used here shared_ptr instead of QSharedPointer for testing purpose)  
template <typename T> 
class Builder : public T 
{ 
public: 
    Builder() = delete; 
    Builder (const Builder&) = delete; 
    template<typename ...Args> 
    static shared_ptr<T> buildObject(Args&&... all) 
    { 
     return make_shared<T>(std::forward<Args>(all)...) ; 
    } 
}; 

class A { };  
class B : public A {}; // Normal class hierarchy. 
         // can't be instantiated if ctor are protected. 

shared_ptr<A> a = Builder<A>::buildObject(); 
shared_ptr<B> b = Builder<B>::buildObject(); 
+0

Warum muss Builder von T erben? In A und B muss Builder als Freund deklariert werden, ist das korrekt? –

+0

@ C.Seidel Builder ist eine Klasse n also keine Notwendigkeit, es als Freund zu erklären. Builder erbt von T, was der Schablonentyp ist (d. H. Es wird nach der Typableitung entweder A oder B sein): Dies gibt ihm Zugriff auf geschützte Mitglieder. Hier eine [Online-Demo] (http://ideone.com/Bfou25) – Christophe

+0

Ihre veröffentlichte Demo funktioniert gut mit std :: shared_ptr. Wenn er jedoch durch QSharedPointer ersetzt wird, beschwert sich der Compiler über den privaten Konstruktor. –

0

Sie könnten von QEnableSharedFromThis erben (siehe here für weitere Details).
Als Beispiel:

class C: public QEnableSharedFromThis<C> { 
    C() = default; 
    // all the other constructors 

public: 
    template<typename ...Args> 
    static QSharedPointer<C> create(Args&&... all) { 
     // refers one of the available constructors 
     return QSharedPointer<C>(new C{std::forward<Args>(all)...}); 
    } 

    QSharedPointer<C> getSharedFromThis() { 
     return sharedFromThis(); 
    } 
}; 

Sie können es als Basisklasse für Ihre Hierarchie verwenden.

+0

Aber ich würde immer noch fast die gleiche create-Methode in allen geerbten Klassen schreiben müssen. Oder liege ich falsch? –

+0

Sie können es tatsächlich als eine Vorlagenklasse definieren. Dies sollte das Problem lösen, die Create-Methode rundum zu definieren. – skypjack

2

Eine einfache Lösung für das Designproblem ist buildObject eine freistehende Funktion Vorlage zu machen, dass Sie eine friend jeder Benutzerklasse zu machen.

Um friend Deklarationen zu vereinfachen, können Sie diese Funktionsvorlage in eine Klasse einfügen.

Dann ist es sehr ähnlich wie Ihr vorhandenen Code, außer es aus der Fabrik Klasse keine Vererbung ist:

#include <memory>  // std::shared_ptr 
#include <utility>  // std::forward 

namespace my{ 
    using std::forward; 
    using std::shared_ptr; 

    template< class Class > 
    struct Make_ 
    { 
     template< class... Args > 
     static auto instance(Args&&... args) 
      -> shared_ptr<Class> 
     { return shared_ptr<Class>(new Class(forward<Args>(args)...)); } 
    }; 

    class A 
    { 
    template<class> friend struct Make_; 
    protected: 
     A(int) {} 
    }; 

    class B 
     : public A 
    { 
    template<class> friend struct Make_; 
    protected: 
     B(int x): A(x) {} 
    }; 

} // namespace my 

auto main() -> int 
{ 
    using namespace my; 
    auto p = Make_<B>::instance(42); 
} 

Allgemeine Lösungen zum unmittelbaren technischen Problem zur Herstellung eine kovarianten Funktion bereitstellt, ohne seine Definition zu wiederholen , enthalten:

  • Ein Makro (das erweitert, um die Funktionsdefinition).
    Dies ist, was ich empfehle, wenn Sie fühlen, dass es unbedingt eine Member-Funktion sein muss. Es ist eine der wenigen legitimen Anwendungen von Makros.

  • Mittelmann-Vererbung.
    Im Wesentlichen anstatt direkt von einem Base und auch von einem Mixin zu erben, erben Sie von dem Mixin und bitten ihn, von Base zu erben. Sie müssen Konstruktorargumente vom Mixin weiterleiten.

  • Dominanz in der virtuellen Vererbungshierarchie.
    Eine wirklich hässliche und komplexe Lösung. Geh nicht dorthin. Aber es ist eine technische Möglichkeit.


Schließlich gibt es nicht-Lösung, die eine Funktion der Verwendung, die einen (smart) Zeiger auf Basisklasse zurückgibt. Wo der Client-Code es ablegen muss. Es ist wirklich nicht gut auf seine Art, aber ich erwähne es für die Vollständigkeit.

Verwandte Themen