简体   繁体   中英

Ensure abstract bass class is a shared_ptr

I have an abstract base class:

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

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

The only valid use case for Base is to live in a shared_ptr - bar is an important method. How can I ensure that the following is impossible:

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

BadDerived bd;
bd.bar(); 

One technique is to make the constructor of Base private and friend a factory class or method:

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>();
}

Usage:

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

This does require you to rewrite any existing derived classes.

Building off of ecatmur's answer , we could also make Base constructible from a type that only has a private constructor:

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{});
}

Every Derived type will have to take an extra constructor argument of type PrivateT that it will have to forward through... but it will still be able to inherit from Base !

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM