class Store
{
public:
    virtual ~Store() = default;

    virtual void process(const QSharedPointer<Action>& action) = 0;

protected:
    Store() = default;
    Store(const Store&) = default;
    Store(Store&&) = default;
    Store& operator=(const Store&) = default;
    Store& operator=(Store&&) = default;
};

如上代码所示,Store类的构造函数用protected修饰,构造函数又用public修饰。这样设计的作用和目的到底是什么呢?

要理解这个Store类的​​构造函数protected+析构函数public​设计,需要结合​​抽象基类(Abstract Base Class, ABC)的设计目标​​、​​C++访问控制语义​​和​​多态安全性​​三个维度分析:

一、先明确类的定位:抽象基类

Store类包含纯虚函数virtual void process(...) = 0,因此是​​抽象基类​​——它的核心作用是定义接口规范,强制派生类实现process逻辑,本身​​不能直接实例化​​(即使构造函数是publicStore s;也会因抽象类而编译失败)。

二、构造函数protected的目的:强化“抽象基类”的语义与约束

构造函数设为protected,本质是​​限制类的实例化路径​​,具体作用:

  1. ​语义明确化​​:

    即使抽象类本身不能实例化,protected构造函数仍向开发者传递强烈的信号——“这个类是用来被继承的,不要尝试直接创建它的对象”。避免误操作(比如新手可能好奇“抽象类能不能实例化?”)。

  2. ​约束派生类构造​​:

    派生类构造时,必须调用基类Store的构造函数(这个操作在Qt Widget开发中经常遇到)。protected保证了​​只有派生类能访问基类构造函数​​,外部代码无法绕过派生类直接构造基类(虽然抽象类本来也做不到,但更严格)。

  3. ​禁止基类拷贝/移动的误用​​:

    类中的拷贝构造、拷贝赋值、移动构造、移动赋值都是protected default——这意味着:

    • 外部代码无法直接拷贝/移动Store对象(符合抽象类的定位);

    • 派生类可以自由拷贝/移动自己的实例(因为派生类能访问基类的protected成员),满足具体派生类的需求(比如复制监控状态)。

三、析构函数public的目的:保证多态安全与资源释放

析构函数设为public是​​多态基类的核心要求​​,否则会导致严重的运行时或编译错误:

  1. ​支持多态销毁​​:

    当用基类指针(如Store* ptr = new MonitorStore();)指向派生类对象时,delete ptr;需要先调用派生类的析构函数,再调用基类的析构函数。如果基类析构函数是privateprotecteddelete表达式​​无法访问基类析构函数​​,会导致编译错误(或未定义行为,如果强制调用的话)。

  2. ​避免资源泄漏​​:

    若基类析构函数非public,派生类对象销毁时,基类的资源(比如成员变量占用的内存、句柄)无法释放,导致资源泄漏。public析构函数确保了“无论通过基类还是派生类指针删除对象,都能正确清理所有资源”。

  3. ​符合C++最佳实践​​:

    C++标准明确要求:​​多态基类的析构函数必须是public virtual​(这里已经满足virtual+public)。这是避免“切片问题”和“销毁不完整”的关键。

四、总结:设计意图的一体两面

访问修饰符

设计目的

构造函数protected

1. 强化抽象基类“不可直接实例化”的语义;2. 约束派生类构造路径;3. 控制拷贝/移动的访问权限。

析构函数public

1. 支持多态销毁(基类指针删派生类对象);2. 保证资源正确释放;3. 遵循多态基类的最佳实践。

补充:为什么不用private构造函数?

如果构造函数是private,派生类也无法访问基类构造函数,导致​​无法继承​​(派生类构造时会报错“无法调用基类的私有构造函数”)。因此protected是“允许继承但禁止直接实例化”的完美平衡。

最终结论

这个设计是​​抽象基类的标准实践​​:

  • protected构造函数确保类只能被继承,不能直接创建对象;

  • public virtual析构函数确保多态销毁的安全性和资源释放的正确性。

这种设计常见于​​框架基类​​(如这里的监控逻辑基类),目的是让派生类专注于实现具体逻辑,同时保证基类的接口规范和多态安全性。

目送一朵云

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐