C++如何透明实现各派生类实例独立计数与自动编号
问题需求
无需重复编写冗余代码,分别统计基类Chara下所有派生类型的实例数量,计数逻辑对派生类的实现完全透明。
预期实现效果如下:
Knight k1; Knight k2; Paladin p1; Paladin p2; k1.name() // Knight 1 k2.name() // Knight 2 p1.name() // Paladin 1 p2.name() // Paladin 2
已尝试的方案
初始Countable模板实现
首先编写了携带静态变量的模板计数类Countable:
template<typename t> class Countable { protected: Countable() { ID = ++count; }; static int count; int ID; }; template<typename t> int Countable<t>::count = 0;
可生效但冗余的实现
当前能实现预期效果的写法,需要每个派生类单独继承对应类型的Countable,自行实现ID拼接逻辑:
class Chara { public: virtual std::string name() const = 0; }; class Knight : public Chara, public Countable<Knight> { std::string name() {return "Knight" + std::to_string(ID);} }; class Paladin : public Chara, public Countable<Paladin> { std::string name() {return "Paladin" + std::to_string(ID);} };
该方案的缺陷是每个派生类都要重复编写模板继承声明与ID取值逻辑,代码复用性差。
下沉到基类的失败尝试
尝试把计数逻辑统一放到Chara基类,派生类仅返回自身类名:
Chara : public Countable<Chara> { public: std::string name() const { return class_name() + " " + std::to_string(ID); } protected: virtual std::string class_name() const = 0; }; Knight : public Chara { std::string class_name() const { return "Knight"; } }; Paladin : public Chara() { std::string class_name() const { return "Paladin"; } };
该方案无法为每个派生类生成独立的模板实例,所有派生类共享同一份计数,运行结果不符合预期:
Knight k1; Knight k2; Paladin p1; Paladin p2; k1.name() // Knight 1 k2.name() // Knight 2 p1.name() // Paladin 3 p2.name() // Paladin 4
解决方案
采用CRTP(奇异递归模板模式)改造基类即可实现需求:将Chara定义为模板类,派生类继承时将自身类型作为模板参数传入基类,编译器会自动为每个派生类生成独立的计数实例,同时ID赋值、名称拼接逻辑全部下沉到基类,派生类无需编写任何计数相关代码。
完整实现代码
#include <string> template<typename T> class Countable { protected: Countable() { ID = ++count; } int ID; // C++17及以上支持类内初始化静态成员,低版本需将初始化移到类外 static inline int count = 0; }; // C++17以下版本放开下方注释即可 // template<typename T> // int Countable<T>::count = 0; template<typename Derived> class Chara : public Countable<Derived> { public: std::string name() const { return static_cast<const Derived*>(this)->class_name() + " " + std::to_string(this->ID); } protected: Chara() = default; }; class Knight : public Chara<Knight> { public: std::string class_name() const { return "Knight"; } }; class Paladin : public Chara<Paladin> { public: std::string class_name() const { return "Paladin"; } };
实现原理
- 每个派生类继承
Chara<自身类型>时,编译器会为不同派生类生成独立的Countable<Derived>模板实例,每个实例持有独立的静态count变量,从根源上避免了所有派生类共享同一份计数的问题 name()接口、ID赋值逻辑全部在基类统一实现,派生类仅需在继承声明时传入自身类型、实现返回类名的接口即可,计数逻辑对派生类完全透明,无冗余代码- 基类构造函数声明为protected,禁止直接实例化基类对象,符合抽象基类的设计要求
测试运行结果完全匹配预期:
Knight k1; Knight k2; Paladin p1; Paladin p2; k1.name(); // Knight 1 k2.name(); // Knight 2 p1.name(); // Paladin 1 p2.name(); // Paladin 2
内容的提问来源于stack exchange,提问作者startresse
相关产品推荐
相关产品推荐

