C++在析构函数和设置新值后访问重写纯虚函数时出现问题

omtl5h9j  于 9个月前  发布在  其他
关注(0)|答案(2)|浏览(96)
#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()错误。

w9apscun

w9apscun1#

documentation中所述
析构函数是一个特殊的成员函数,在对象的生存期结束时调用。
虽然有时它可以被显式调用,但这种情况非常罕见且非常复杂。由于调用了析构对象的方法并调用了两次析构函数,您的代码表达了多个未定义行为的情况:
析构函数也可以被直接调用,例如,销毁一个使用placement-new构造的对象,或者通过一个分配器成员函数(如std::allocator::destroy()),销毁一个通过分配器构造的对象。**注意,直接为一个普通对象(如局部变量)调用析构函数,当在作用域的末尾再次调用析构函数时,会调用未定义的行为。
(重点是我的)
因此,简单的解决方案是使用普通方法,可能在您的情况下受到保护,该方法释放数组并从析构函数和函数中调用它:

class Abstract {
protected:
    int* arr;
    int size;

    void deallocate()
    {
        delete[] arr; // Note: delete on nullptr is a Noop
        arr = nullptr;
    }
public:
    Abstract(int s = 10) 
    {
        size = s;
        arr = new int[size];
    }
    virtual ~Abstract()
    {
        deallocate();
    }
    virtual void foo() = 0;
    void func() { foo(); }
};

字符串
然后在派生类中使用相同的函数。为了更干净的代码,我还将添加受保护的方法void allocate( size_t size ),并将成员arrsize移动到基类的私有区域。
注意事项:我假设你做这个练习是为了更好地理解C++中的工作原理,所以我相应地回答了。在真实的代码中,尽管应该避免拥有内存的原始指针,而应该使用智能指针或适当的容器来消除代码中的许多潜在问题(例如,您的类违反了The rule of three/five/zero)。

ylamdve6

ylamdve62#

我强烈建议 * 不要 * 在析构函数和原始指针周围做任何令人惊讶的事情:

#include <cstdint>
#include <iostream>
#include <memory>

class Abstract {
 protected:
  size_t size;
  std::unique_ptr<int[]> arr;

 public:
  Abstract(size_t size = 10) : size{size}, arr{std::make_unique<int[]>(size)} {}
  virtual void foo() = 0;
  void func() { foo(); }
};

class Derived : public Abstract {
 public:
  using Abstract::Abstract;
  void newArr() { arr = std::make_unique<int[]>(size); }
  void foo() override {}
};

int main() {
  Derived d;
  d.func();
  d.newArr();
  d.func();

  d = Derived{20};  // frees memory properly!
  d.func();
  d.newArr();
  d.func();
}

字符串
当使用原始指针时,很容易忘记一个operator =()或类似的东西,这会到处泄漏内存。智能指针提供了适当的构造函数,析构函数和运算符。

相关问题