可以在C ++中声明基类,使得从它派生的类只能通过创建函数创建吗?

时间:2018-01-18 00:11:46

标签: c++ inheritance

类似于以下内容

class Base
{
protected:
    Base()
    {}

public:
    virtual void initialize()
    {}

    template<typename D, typename... Ts>
    static std::shared_ptr<D> create(Ts... args)
    {
        auto d = std::shared_ptr<D>(new D(args...)); 
        d->initialize();
        return d;
    }
};

class Derived : public Base
{
    private:
        int value_;

    protected:

    public:

        Derived(int v) : value_(v)
        {}

        void initialize() override
        {
            //Do something with value_
        }
};


int main()
{
    auto derived = Base::create<Derived>(42);
    // etc.

    return 0;
}

上面按预期工作但如果Base是库的用户通过实现Derived扩展的类库的一部分,那么没有什么能阻止库的用户通过其公共构造函数创建Derived,从而初始化won& #39;被叫。

在这样的库/库用户场景中,如何为Derived类创建强制使用Base :: create(...)之类的东西?

例如,如果将Base :: Base()设为私有而不是受保护,则代码不会被编译,因为Derived :: Derived()将无法访问它。

2 个答案:

答案 0 :(得分:1)

如果您的目标只是确保派生类调用初始化函数使其成为纯虚拟。

我建议使用非虚拟接口(NVI)习语。

通过这种方式,库用户被强制在派生类中实现doInitialize函数。

actiongroup = gtk.ActionGroup('window-clip-actions')
accelgroup = gtk.AccelGroup()
fake_toolbar = gtk.Toolbar()
view.get_window().add_accel_group(accelgroup)
view.get_window().get_content_area().pack_start(fake_toolbar)
for shortcut, cb in (('<ctrl><shift>c', self.on_window_clip_copy),
                     ('<ctrl><shift>v', self.on_window_clip_paste)):
    action = gtk.Action(shortcut, shortcut, 'clip-action', None)
    actiongroup.add_action_with_accel(action, shortcut)
    action.connect("activate", cb)
    action.set_accel_group(accelgroup)
    action.connect_accelerator()
    toolitem = action.create_tool_item()
    fake_toolbar.insert(toolitem, -1)
fake_toolbar.set_visible(False)

答案 1 :(得分:1)

这个解决方案有点挑剔,但我认为它涵盖了所有基础:

class Base
{
protected:

    // Token is only visible to Derived, and only constructible by Base
    class token {
        friend Base;
        token() {};
    };

    // Constructing Base requires a token
    Base(token)
    {}

public:
    // bla

    template<typename D, typename... Ts>
    static std::shared_ptr<D> create(Ts... args)
    {
        auto d = std::shared_ptr<D>(new D({}, args...));
        // Token is created here          ^^ 

        // bla
    }
};

class Derived : public Base
{
    // bla

    public:

        // Derived receives and forwards the token to Base
        // The implicit copy constructor is accessible
        Derived(token t, int v) : Base(t), value_(v)
        {}

    // bla
};

因此,没有人可以写Derived d(Base::token{}, 42)因为token无法访问。 Derived本身可以命名token并传递一个,但不能创建一个:只有Base的{​​{1}}函数可以。