2

次のプログラムがクラッシュするのはなぜですか? デストラクタが仮想ではなく、子クラスのデストラクタが仮想である基本クラスがあります。

#include <iostream>

class Base {
public:
  Base() {
    std::cout << "Base::Base CTOR " << std::endl;
  }
  ~Base() {
    std::cout << "Base::Base DTOR " << std::endl;
  }
};

class Child : public Base {
public:
  Child(){
    std::cout << "Child::Child CTOR " << std::endl;
  }
  virtual ~Child() {
    std::cout << "Child::Child DTOR " << std::endl;
  }

};

int main (int argc, char **argv) {
  Base *ptr = new Child;
  delete ptr;
}
4

4 に答える 4

5

削除するポインター オペランドの静的な型が、それが指すオブジェクトの動的な型と一致せず、基本クラスにポインターを渡すことを許可するこの規則の例外の要件を満たしていないため、動作が未定義です。この例外では基本クラスに仮想デストラクタが必要なため、削除されるオブジェクトに。

「期待どおり」に動作するコードやクラッシュなど、あらゆる動作が可能です。

于 2012-06-25T05:27:12.323 に答える
1

これを見てください:

#include <iostream>

class Base
{
public:
    void nonvirtualmethod()
    { std::cout << "Base nonvirtualmethod" << std::endl; }
    virtual void virtualmethod()
    { std::cout << "Base virtualmethod" << std::endl; }
};

class Derived: public Base
{
public:
    void nonvirtualmethod()
    { std::cout << "Derived nonvirtualmethod" << std::endl; }
    virtual void virtualmethod()
    { std::cout << "Derived virtualmethod" << std::endl; }
};

int main()
{
    Derived d;
    Derived* pd = &d;
    Base* pb = &d;    //< NOTE: both pd and pb point to the same object

    pd->nonvirtualmethod();
    pb->nonvirtualmethod();
    pd->virtualmethod();
    pb->virtualmethod();
}

次の出力を提供します。

Derived nonvirtualmethod
Base nonvirtualmethod
Derived virtualmethod
Derived virtualmethod  //< invoked by a Base*

pbこれは、ポインタの静的型(Base*)とポインタが指す動的型()に違いがあるためDerivedです。仮想メソッドとプレーンメソッドの違いは、非仮想メソッドは静的型マッピングに従う(つまり、BaseポインターはBase::メソッドを呼び出す)のに対し、仮想メソッドは実行時型のチェーンに従うため、をBase*指す場合DerivedDerivedメソッドが呼び出されることです。 。

この意味で、デストラクタは特別なものではありません。仮想でない場合、Baseポインタはデストラクタを呼び出さないためDerived、メモリストアに戻される、半分破壊されたオブジェクトが残ります。

これがUBである(そして単に否定されていない)理由は、「メモリストア」が言語自体によって管理されていないためですが、プログラムがホストされているプラ​​ットフォームからです。クラッシュは、そのDerived部分(まだ生きている)は、オペレーティングシステムが間違った開始アドレスでメモリのブロックを解放しようとする結果になります。

于 2012-06-25T06:56:26.817 に答える
1

この例がポイントを理解するのに役立つことを願っています:

#include <iostream>
class Base {
public:
 Base() {
    std::cout << "Base::Base CTOR " << std::endl;
 }
 ~Base() {
   std::cout << "Base::Base DTOR " << std::endl;
 }
private:
protected:
};

class Child : public Base {
 public:
 Child(){
std::cout << "Child::Child CTOR " << std::endl;
  }
  ~Child(){
std::cout << "Child::Child DTOR " << std::endl;
 }
  private:
 protected:
 };
  class gChild : public Child {
   public:
   gChild(){
    std::cout << "Child::Child gCTOR " << std::endl;
   }
  ~gChild(){
    std::cout << "Child::Child gDTOR " << std::endl;
  }
private:
protected:
};
int main ( int argc, char **argv) {
    Base *ptr = new gChild;
 delete ptr;
}

virtual ~Base() の場合、すべてのデストラクタの出力が出力されます。

virtual ~child() または virtual ~gChild() の場合、基本デストラクタのみが出力されます。

これは、デストラクタが反対方向に実行されるためです。ここでの動作は未定義です。期待される結果を得るには、ベース デストラクタ virtual を定義する必要があります。

ありがとう。

于 2012-06-25T05:45:43.467 に答える