C++:如何通过基类限制派生类自定义构造函数?
问题解决方案
可行性结论
仅修改Entity和EntityFactory<typename T>两个引擎类,无法从语法上完全禁止前端派生类定义可绕过工厂的构造函数——C++语法允许派生类自主定义构造函数的访问权限,基类无法直接限制派生类的这一行为。但我们可以通过修改引擎类,让前端派生类必须遵循工厂构造的规则,否则编译报错,从而实现强制只能通过工厂创建对象的效果。
实现方案
方案一:私有标记参数限制构造
通过在Entity中定义一个私有嵌套类作为构造标记,只有EntityFactory能创建该标记,派生类必须接收此标记才能完成构造,从而阻断外部直接构造的路径:
// 引擎类:Entity class Entity { private: // 私有构造标记,仅EntityFactory可实例化 struct FactoryTag { explicit FactoryTag() = default; friend class EntityFactory<Entity>; template<typename Derived> friend class EntityFactory; }; protected: // 受保护构造函数,必须传入FactoryTag explicit Entity(FactoryTag) {} public: virtual ~Entity() = default; }; // 引擎类:EntityFactory template<typename T> class EntityFactory { public: static T* Create() { // 仅此处能合法创建FactoryTag,调用派生类构造 return new T(typename Entity::FactoryTag{}); } }; // 前端派生类示例:Man class Man : public Entity { public: // 必须接收FactoryTag参数,外部无法自行构造该参数 Man(Entity::FactoryTag tag) : Entity(tag) {} }; // 合法调用 Man* player = EntityFactory<Man>::Create(); // 非法:外部无法访问Entity::FactoryTag,无法构造Man // Man* invalid = new Man(Entity::FactoryTag{});
此方案中,前端派生类如果不定义接收FactoryTag的构造函数,会因无法调用Entity的私有默认构造而编译失败,从而强制遵循规则。
方案二:CRTP简化派生类实现
利用奇异递归模板模式(CRTP)封装构造逻辑,让前端派生类无需手动编写构造函数,进一步降低使用成本:
// 引擎类:Entity class Entity { private: struct FactoryTag { explicit FactoryTag() = default; friend class EntityFactory<Entity>; template<typename Derived> friend class EntityFactory; }; protected: Entity(FactoryTag) {} public: virtual ~Entity() = default; }; // 引擎类:CRTP基类,封装构造逻辑 template<typename Derived> class EntityCRTP : public Entity { protected: // 仅EntityFactory<Derived>可调用此构造 EntityCRTP() : Entity(typename Entity::FactoryTag{}) {} friend class EntityFactory<Derived>; }; // 引擎类:EntityFactory template<typename T> class EntityFactory { public: static T* Create() { return new T(); } }; // 前端派生类示例:Man class Man : public EntityCRTP<Man> { // 无需自定义构造函数,继承的构造仅工厂可调用 }; // 合法调用 Man* npc = EntityFactory<Man>::Create(); // 非法:EntityCRTP的构造为protected,且仅工厂是友元 // Man* invalid = new Man();
此方案中,前端派生类只需继承EntityCRTP<Derived>即可自动获得工厂专属的构造能力,无需额外代码。
内容的提问来源于stack exchange,提问作者Spring E. Thing
相关产品推荐
相关产品推荐

