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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.07 16:15:42