2015-04-16 7 views
8

Mam abstrakcyjna klasa podstawowa:Zapewnienie klasa abstrakcyjna bas jest shared_ptr

struct Base : std::enable_shared_from_this<Base> 
{ 
    virtual ~Base() = default; 
    virtual void foo() = 0; 

    void bar() { 
     baz(shared_from_this()); 
    } 
}; 

Jedyny ważny przypadek użycia dla Base jest żyć w shared_ptr - bar jest ważną metodą. Skąd mogę mieć pewność, że następuje to niemożliwe:

struct BadDerived : Base { 
    void foo() override { ... } 
}; 

BadDerived bd; 
bd.bar(); 
+0

Czy myślisz o używaniu nie-wirtualnego interfejsu? http://stackoverflow.com/a/2735596/4342498 – NathanOliver

+0

Jeśli byłby sposób na zrobienie tego, 'enable_shared_from_this' już to zrobiłoby, a twoja klasa dostałaby to za darmo. –

+0

@vsoftco Nie chcę być w stanie zbudować 'Base' poza' shared_ptr', aby 'bar()' nie mógł być wywołany poza 'shared_ptr'. – Barry

Odpowiedz

5

Jedną z technik jest, aby konstruktor Base prywatnego i friend klasa fabryki lub metoda:

struct Base : std::enable_shared_from_this<Base> 
{ 
    virtual ~Base() = default; 
    virtual void foo() = 0; 

    void bar() { 
     baz(shared_from_this()); 
    } 

private: 
    template<class Impl> friend std::shared_ptr<Base> makeDerived(); 
    Base() {} 
}; 

template<class Impl> 
std::shared_ptr<Base> makeDerived() { 
    struct Derived : Base, Impl { 
     void foo() override { Impl::foo(static_cast<Base*>(this)); } 
    }; 
    return std::make_shared<Derived>(); 
} 

Zastosowanie:

struct Impl { 
    void foo(Base* self) { std::cout << "Hello!" << std::endl; } 
}; 
auto gd = makeDerived<Impl>(); 
gd->bar(); 

Wymaga to przepisania istniejących klas pochodnych.

0

budynku off ecatmur's answer, możemy również dokonać Base konstruowalnych od typu, który ma tylko prywatny konstruktor:

class PrivateT { 
    PrivateT() { } 

    template <typename Impl, typename... Args> 
    friend std::shared_ptr<Impl> makeDerived(Args&&...); 
}; 

struct Base : std::enable_shared_from_this<Base> { 
    Base(PrivateT) { } 
    virtual void foo() = 0; 

    void bar() { 
     baz(shared_from_this()); 
    } 
}; 

template <typename Impl, typename... Args> 
std::shared_ptr<Impl> makeDerived(Args&&... args) { 
    return std::make_shared<Impl>(std::forward<Args>(args)..., 
     PrivateT{}); 
} 

Każdy Derived typu będą musiały podjąć dodatkowe konstruktora argumentu typu PrivateT że będzie miał przejść dalej ... ale nadal będzie mógł odziedziczyć po Base!

struct Impl : Base { 
    Impl(PrivateT pt) : Base(pt) { } 
    void foo() override { std::cout << "Hello!" << std::endl; } 
}; 

auto gd = makeDerived<Impl>(); 
gd->bar(); 
+0

dlaczego nie używać idiomu korpusu uchwytu? jeśli mówisz, że sens ma być wspólnym wskaźnikiem, zamiast funkcji przyjaciela użyj konstruktora. i ustaw obiekt jako współdzielony wskaźnik jako kompozycję. w ten sposób nikt nie widzi współużytkowanego wskaźnika i możesz egzekwować te zasady. – Alex

+0

@Alex Nie widzę, jak to rozwiązałoby problem. – Barry

+0

http://ideone.com/41O3s7 odtwarzane z nim. Najważniejszą rzeczą, jaką powinien zrobić, jest powstrzymanie ludzi przed wpisywaniem 'x = * Base', ponieważ wskaźnik jest niedostępny. ale to tylko cukier syntaktyczny. – Alex