含用户声明析构函数的基类引发派生类拷贝构造问题求解
解决C++基类删除拷贝构造后派生类拷贝构造失效的问题
咱们先理清问题的来龙去脉:
你最初的代码里,Base类因为声明了用户自定义的虚析构函数,Clang会警告隐式生成的拷贝构造函数被弃用——这是C++标准的规定:当类有用户声明的析构函数时,编译器生成的拷贝构造/赋值运算符会被标记为deprecated,这类场景很容易伴随对象切片等风险。
当你把Base的拷贝构造和赋值运算符直接delete后,派生类Derived的隐式拷贝构造会被自动删除(因为派生类的拷贝构造需要调用基类的拷贝构造,而基类的是delete状态),即使你显式= default也没用,因为基类的拷贝构造无法被调用。
正确的解决方案:将Base的拷贝构造/赋值运算符设为protected
不要直接delete基类的拷贝构造,而是把它们声明为protected,这样既可以避免外部代码拷贝Base类型的对象(防止切片),又允许派生类的拷贝构造正常调用基类的拷贝逻辑:
class Base { public: Base() = default; virtual ~Base() = default; virtual void print() = 0; protected: // 允许派生类调用,外部无法访问 Base(const Base&) = default; Base& operator=(const Base&) = default; }; class Derived : public Base { public: Derived() { stuff.resize(1000); } void print() override { cout << "Whatever\n"; } // 现在可以正常默认,或者让编译器隐式生成 Derived(const Derived&) = default; Derived& operator=(const Derived&) = default; private: std::vector<int> stuff; };
为什么这个方案可行?
- 解决Clang警告:因为我们显式声明了基类的拷贝构造/赋值运算符,编译器不会再生成被弃用的隐式版本,警告自然消失。
- 避免对象切片:
protected访问权限确保外部代码无法直接拷贝Base对象(比如Base b = d1;这种切片操作会编译报错),符合你最初想要避免拷贝Base引发问题的初衷。 - 派生类正常拷贝:派生类的拷贝构造可以合法调用基类的
protected拷贝构造,同时自己的成员(比如std::vector<int> stuff)会被正确拷贝,完全满足你代码中Derived d2 = d1;的需求。
要不要忽略原警告?
绝对不建议忽略!这个警告是C++标准委员会为了避免潜在的对象切片和资源管理问题而引入的,忽略它可能会在后续代码维护中埋下隐患——比如如果后续新增了非抽象的派生类,不小心用基类类型拷贝派生类对象,就会触发切片,导致数据丢失或行为异常。
内容的提问来源于stack exchange,提问作者Timmmm
相关产品推荐
相关产品推荐

