You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何强制继承抽象类的子类使用私有构造与静态工厂函数?

强制子类遵循std::enable_shared_from_this的私有构造+工厂函数实现方案

我正在编写一个继承自std::enable_shared_from_this的抽象类,会有多个子类继承它。根据cppreference的示例,使用这类类的最佳实践是将构造函数设为私有(示例代码如下),但现在想知道如何强制子类必须将构造函数设为私有并声明静态工厂函数,或者除了写注释之外,有没有其他方式提示开发者正确实现(已知C++没有虚静态函数)。

class Best : public std::enable_shared_from_this<Best>
{
    struct Private{ explicit Private() = default; };
 
public:
    // Constructor is only usable by this class
    Best(Private) {}
 
    // Everyone else has to use this factory function
    // Hence all Best objects will be contained in shared_ptr
    static std::shared_ptr<Best> create()
    {
        return std::make_shared<Best>(Private());
    }
 
    std::shared_ptr<Best> getptr()
    {
        return shared_from_this();
    }
};

可行的实现方案

1. CRTP(奇异递归模板模式)+友元强制工厂创建

通过CRTP让抽象基类绑定子类类型,将基类构造设为保护,同时让基类成为子类的友元,仅允许基类的工厂函数创建子类实例,间接强制子类隐藏自身构造。

#include <memory>

template <typename Derived>
class Base : public std::enable_shared_from_this<Derived>
{
protected:
    Base() = default;

public:
    // 统一工厂接口,强制子类通过此创建实例
    template <typename... Args>
    static std::shared_ptr<Derived> create(Args&&... args)
    {
        return std::make_shared<Derived>(std::forward<Args>(args)...);
    }

    // 禁用拷贝移动,避免裸对象流转
    Base(const Base&) = delete;
    Base& operator=(const Base&) = delete;
    Base(Base&&) = delete;
    Base& operator=(Base&&) = delete;
};

// 子类示例
class Derived : public Base<Derived>
{
    // 必须将构造设为私有/保护,否则外部仍可直接创建
    Derived(int value) : m_value(value) {}
    
    // 让基类访问私有构造
    friend class Base<Derived>;

    int m_value;

public:
    void print() const { /* ... */ }
};

// 使用方式
int main()
{
    auto ptr = Derived::create(42);
    // Derived d(42); // 编译错误:构造函数私有
}

2. 私有标签类限制构造访问

模仿cppreference的思路,在基类中定义私有标签结构体,要求子类构造必须接收该标签,而只有基类工厂能生成标签,从根源上阻止外部直接实例化子类。

#include <memory>

class Base : public std::enable_shared_from_this<Base>
{
    // 私有标签,仅基类内部可创建
    struct PrivateTag { explicit PrivateTag() = default; };

protected:
    Base(PrivateTag) = default;

public:
    virtual ~Base() = default;

    // 泛型工厂函数,支持所有子类
    template <typename Derived, typename... Args>
    static std::shared_ptr<Derived> create(Args&&... args)
    {
        return std::make_shared<Derived>(PrivateTag{}, std::forward<Args>(args)...);
    }

    // 禁用拷贝移动
    Base(const Base&) = delete;
    Base& operator=(const Base&) = delete;
};

class Derived : public Base
{
    // 子类构造必须接收私有标签,且设为私有
    Derived(PrivateTag tag, int value) : Base(std::move(tag)), m_value(value) {}
    
    friend class Base;

    int m_value;

public:
    void doSomething() { /* ... */ }
};

// 使用方式
int main()
{
    auto ptr = Base::create<Derived>(42);
    // Derived d(42); // 编译错误:无匹配构造函数
}

3. 编译期检查辅助

结合static_assert和类型特性,在编译阶段检查子类是否可被外部直接构造,若违规则触发编译错误,强化规范约束。

#include <memory>
#include <type_traits>

template <typename Derived>
class Base : public std::enable_shared_from_this<Derived>
{
protected:
    Base() 
    {
        // 编译期断言:外部无法直接构造子类
        static_assert(!std::is_constructible_v<Derived>, "子类必须将构造函数设为私有/保护");
    }
};

总结

C++语法无法做到100%强制子类遵循规范,但通过CRTP+友元+私有标签的组合,可以极大程度限制错误实例化行为,配合编译期检查和清晰注释,能有效引导开发者正确实现。

内容的提问来源于stack exchange,提问作者Nkamil

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 06:11:13