#include <iostream>
using namespace std;
class Abstract {
protected:
int* arr;
int size;
public:
Abstract(int s = 10)
{
size = s;
arr = new int[size];
}
virtual ~Abstract()
{
if (arr)
{
delete[] arr;
}
arr = nullptr;
}
virtual void foo() = 0;
void func() { foo(); }
};
class Derived : public Abstract
{
public:
void newArr()
{
Abstract::~Abstract(); // <=> this->~Derived()
arr = new int[size];
}
virtual void foo()override {};
};
int main() {
Derived d;
d.func();//ok
d.newArr();//ok
d.func();//error
return 0;
}
字符串
在调用抽象类的distractor,并从派生类中设置新值为foo后,下一次抽象类使用纯虚函数foo时,它不会重定向到派生的覆盖实现,并得到debug abort()错误。
2条答案
按热度按时间w9apscun1#
如documentation中所述
析构函数是一个特殊的成员函数,在对象的生存期结束时调用。
虽然有时它可以被显式调用,但这种情况非常罕见且非常复杂。由于调用了析构对象的方法并调用了两次析构函数,您的代码表达了多个未定义行为的情况:
析构函数也可以被直接调用,例如,销毁一个使用placement-new构造的对象,或者通过一个分配器成员函数(如std::allocator::destroy()),销毁一个通过分配器构造的对象。**注意,直接为一个普通对象(如局部变量)调用析构函数,当在作用域的末尾再次调用析构函数时,会调用未定义的行为。
(重点是我的)
因此,简单的解决方案是使用普通方法,可能在您的情况下受到保护,该方法释放数组并从析构函数和函数中调用它:
字符串
然后在派生类中使用相同的函数。为了更干净的代码,我还将添加受保护的方法
void allocate( size_t size )
,并将成员arr
和size
移动到基类的私有区域。注意事项:我假设你做这个练习是为了更好地理解C++中的工作原理,所以我相应地回答了。在真实的代码中,尽管应该避免拥有内存的原始指针,而应该使用智能指针或适当的容器来消除代码中的许多潜在问题(例如,您的类违反了The rule of three/five/zero)。
ylamdve62#
我强烈建议 * 不要 * 在析构函数和原始指针周围做任何令人惊讶的事情:
字符串
当使用原始指针时,很容易忘记一个
operator =()
或类似的东西,这会到处泄漏内存。智能指针提供了适当的构造函数,析构函数和运算符。