16
class Widget
{
    public:
        Widget() {
            cout<<"~Widget()"<<endl;
        }
        ~Widget() {
            cout<<"~Widget()"<<endl;
        }

    void* operator new(size_t sz) throw(bad_alloc) {
        cout<<"operator new"<<endl;
        throw bad_alloc();
    }

    void operator delete(void *v) {
        cout<<"operator delete"<<endl;
    }

};

int main() 
{
    Widget* w = 0;
    try {
        w = new Widget();
    }
    catch(bad_alloc) {
        cout<<"Out of Memory"<<endl;
    }

    delete w;
    getch();
    return 1;
}

このコードでは、デストラクタがある場合、delete wオーバーロードされた演算子を呼び出しません。deleteデストラクタを省略すると、オーバーロードされたものdeleteが呼び出されます。これはなぜですか?

~Widget() 書き込み時の出力

operator new
メモリ不足

~Widget() が書かれていない場合の出力

operator new
メモリ不足
operator delete

4

7 に答える 7

21

少し前のcomp.lang.c++.moderatedの演算子削除で似たようなことを覚えています。私は今それを見つけることができませんが、答えはこのようなことを述べました..

残念ながら、対応する型の null ポインターで delete-expression が呼び出されたときに、コントロールがオーバーロードされた 'operator delete' に入るべきかどうかについて、言語仕様は十分に明確ではありません。 -pointer はノーオペレーションです。

James Kanzeは具体的に次のように述べています。

チェックするのは、オペレーターの削除 (または削除[]) の責任です。標準は、null ポインターが与えられないことを保証しません。標準では、null ポインターが指定された場合、ノーオペレーションである必要があります。または、実装がそれを呼び出すことが許可されていること。最新のドラフトによると、「割り当て解除関数に指定された最初の引数の値は null ポインター値である可能性があります。その場合、割り当て解除関数が標準ライブラリで提供されている場合、呼び出しは効果がありません。」「標準ライブラリで提供されるものである」という意味が何を意味するのかはよくわかりません---文字通りに解釈されます.彼の機能は標準ライブラリによって提供されるものではないため、文は適用されないようです. . でも、なんとなく意味がわからない

これは、以前に同様の問題があり、回答を .txt ファイルに保存していたので覚えています。

更新-1:

ああ、ここで見つけました。このリンク障害レポートもお読みください。したがって、答えはUnspecifiedです。第5.3.5/7章。

于 2009-07-10T09:04:40.480 に答える
9

まず第一に、これは本当に単純化できますdelete (Widget*)0- あなたの他のすべてはこれを再現するのmain()に不要です.

これは、1) ユーザー定義operator deleteが NULL 値を処理できる必要があり、2) コンパイラが可能な限り最適なコードを生成しようとするために発生するコード生成アーティファクトです。

まず、ユーザー定義のデストラクタが含まれていない場合を考えてみましょう。その場合、インスタンスで実行するコードはありません。ただし、operator delete. コントロールを に転送する前に null をチェックしても意味がありませoperator deleteん。後者はとにかくチェックを行う必要があるためです。そのため、コンパイラは無条件の呼び出しを生成するだけですoperator delete(そして、後者がメッセージを出力するのがわかります)。

2 番目のケース - デストラクタが定義されました。これは、deleteステートメントが実際には 2 つの呼び出し (デストラクタとoperator delete. ただし、デストラクタはクラス フィールドにアクセスしようとする可能性があるため、null ポインタで安全に呼び出すことはできません (コンパイラは、特定のデストラクタが実際にはそれを行わないため、 nullthisで呼び出しても安全であると判断できますが、そうではないように見えます)。実際には気にしません)。したがって、デストラクタ呼び出しの前に null チェックが挿入されます。そして、チェックがすでにそこにある場合は、それを使用して への呼び出しをスキップすることoperator deleteもできます-とにかく何もしない必要がありoperator delete、ポインタが実際に無効です。

私が見る限り、ISO C++ 仕様によって保証されているものは何もありません。ここでは、両方のコンパイラが同じ最適化を行うだけです。

于 2009-07-10T09:37:08.247 に答える
4

その理由は、デストラクタがある場合、削除演算子の呼び出しがスカラー削除デストラクタ内から行われるためです。VC には、デストラクタと削除演算子の両方への呼び出しが含まれています。コンパイラは、NULL ポインターを削除しようとしているかどうかを確認するコードを提供します。もちろん、そのようなポインターの削除は合法ですが、メンバー変数の使用が含まれている可能性があるため、そのようなオブジェクトのデストラクタを呼び出してはなりません。そのため、スカラー削除デストラクタの呼び出しが回避され、その結果、削除演算子の呼び出しも回避されます。

デストラクタがない場合、コンパイラは、スカラ削除デストラクタを生成せずに、delete 演算子を直接呼び出すだけです。したがって、そのような場合、やはり削除演算子が呼び出されます。

于 2009-07-10T09:06:42.363 に答える
3

回答ではなく、コメントを残したいのですが、新しいメンバーとして十分な権限がありませんでした。

オブジェクトの作成中に例外が発生しています。オブジェクト自体が作成されていないため、デストラクタは呼び出されません。

コンストラクタとデストラクタからのメッセージが表示されないため、これも観察できます。

ただし、デストラクタが定義されていないときに削除が呼び出されています。destrcutor が定義されていない場合、C++ コンパイラはそれを他の演算子と見なすと考えられる場合、定義されていない場合、コンパイラはデフォルトでデストラクタを提供します。

于 2009-07-10T09:44:53.130 に答える
3

良い答えはありませんが、問題を少し単純化しました。次のコードは、演算子 new と例外処理を削除します。

#include <iostream>
using namespace std;

class Widget {

  public:
    Widget() {
        cout<<"Widget()"<<endl;
    }
    ~Widget() {
        cout<<"~Widget()"<<endl;
    }

  void operator delete(void *v) {
       cout << "operator delete" << endl;
  }
};

int main() {
    Widget* w = 0;
    cout << "calling delete" << endl;
    delete w;
}

これは、VC++ と g++ の両方で同じ動作を示します。

もちろん、NULL ポインターの削除はノーオペレーションであるため、コンパイラーは演算子 delete を呼び出す必要はありません。実際にオブジェクトを割り当てる場合:

    Widget* w = new Widget;

その後、物事は期待どおりに機能します。

于 2009-07-10T08:50:13.973 に答える
-1

オブジェクト デストラクタは、削除演算子の前に呼び出されます。したがって、私の推測では、デストラクタを呼び出そうとし、ポインタが NULL であることを認識しているため、

  1. インスタンスを必要とするデストラクタを呼び出さない
  2. そこで削除操作を停止します(一種の速度最適化IMHO)。

Neil が言ったように、w に Widget が含まれていれば、動作するはずです。

于 2009-07-10T09:07:54.127 に答える
-2

NULL ポインターを削除しようとしました。そのため、デストラクタは呼び出されませんでした。

class Widget
{   
public:        
    Widget()
    {            
        cout<<"Widget()"<<endl;        
    }       

    ~Widget() 
    {          
        cout<<"~Widget()"<<endl;    
    }    

    void* operator new(size_t sz) throw(bad_alloc) 
    {      
        cout<<"operator new"<<endl;  
        return malloc(sizeof(Widget));
        //throw bad_alloc();    
    }  

    void operator delete(void *v)
    {               
        cout<<"operator delete"<<endl;   
    }
};

int main()
{

    Widget* w = NULL; 
    try 
    {   
        w = new Widget();
        //throw bad_alloc();
    }   
    catch(bad_alloc) 
    {        
        cout<<"Out of Memory"<<endl;  
    }   
    delete w; 
}

出力:

operator new
Widget()
~Widget()
operator delete

于 2009-07-10T09:01:09.317 に答える