如何正确使用抽象类的右值引用成员?解决abort()报错问题
问题分析与解决方案
崩溃原因
你遇到的崩溃核心是悬垂引用问题,和所谓的“类型从Derived变Base”无关:
- 临时对象
Derived(1,1)在Bag bag(Derived(1,1));执行时,会被绑定到Bag构造函数的Base&& b参数,但临时对象的生命周期不会因为绑定到类的成员引用而延长——只有绑定到局部的const引用/右值引用变量时,生命周期才会同步延长。因此Bag构造完成后,这个临时Derived对象会立刻被销毁。 - 因为Base有虚析构函数,销毁Derived对象时会按正确的多态析构顺序:先调用Derived的析构函数(释放自身的
_arr),再调用Base的析构函数(释放Base的_arr),整个对象的内存被完全回收。此时Bag类中的_b就是指向已释放内存的悬垂引用,调用_b.func()时,程序试图通过虚函数表访问已销毁的对象,直接触发abort报错。 - 删掉Base的虚析构后“能运行”是未定义行为的侥幸结果:没有虚析构时,编译器会按静态类型(Base)调用析构函数,Derived的析构不会被执行,对象内存可能未被完全回收,虚函数表地址碰巧还在原位置,才让func调用“看起来正常”,但这完全不可靠,随时可能崩溃。
保留虚析构的修改方案
不能用引用成员来持有多态临时对象,应该用智能指针管理对象的生命周期,推荐使用std::unique_ptr(独占所有权,轻量高效):
修改后的完整代码
#include <iostream> #include <memory> using namespace std; class Base { public: Base(int n) :_n(n) { _arr = new int[n]; } virtual ~Base() { delete[] _arr; } Base(Base&& other) { _n = other._n; _arr = other._arr; other._arr = nullptr; other._n = 0; } virtual void func() = 0; private: int _n; int* _arr; }; class Derived : public Base { public: Derived(int m, int n) : Base(n), _m(m) { _arr = new int[m]; } ~Derived() { delete[] _arr; } Derived(Derived&& other) : Base(std::move(other)) { _m = other._m; _arr = other._arr; other._arr = nullptr; other._m = 0; } void func() override { cout << "func"; } private: int _m; int* _arr; }; class Bag { public: // 接收unique_ptr并转移所有权 Bag(std::unique_ptr<Base> b) : _b(std::move(b)) {} void func() { _b->func(); } private: std::unique_ptr<Base> _b; // 用智能指针持有多态对象 }; int main() { // 创建Derived对象并转移所有权给Bag Bag bag(std::make_unique<Derived>(1, 1)); bag.func(); }
关键说明
std::unique_ptr<Base>可以正确管理多态对象的生命周期,当Bag被销毁时,会自动调用Base的虚析构函数,进而触发Derived的析构函数,保证内存正确释放。- 用
std::make_unique创建对象是更安全的写法(避免内存泄漏风险),也可以用std::unique_ptr<Base>(new Derived(1,1))替代。
内容的提问来源于stack exchange,提问作者Ryime
相关产品推荐
相关产品推荐

