2012-01-06 10 views
29
#include <iostream> 
using namespace std; 

class CPolygon { 
    protected: 
    int width, height; 
    public: 
    virtual int area() 
     { return (0); } 
    }; 

class CRectangle: public CPolygon { 
    public: 
    int area() { return (width * height); } 
    }; 

は、この警告を理解するためにどのようにして、コードを改善するために、どのようにコンパイル警告C++コンパイル中に '仮想メソッド...ただし非仮想デストラクタ'警告の意味は何ですか?

Class '[[email protected]' has virtual method 'area' but non-virtual destructor 

ていますか?

[EDIT]は現在このバージョンが正しいですか?それはあなたが仮想メソッドを持つ基本クラスに仮想デストラクタを必要とする意味

#include <iostream> 
using namespace std; 

class CPolygon { 
    protected: 
    int width, height; 
    public: 
    virtual ~CPolygon(){}; 
    virtual int area() 
     { return (0); } 
    }; 

class CRectangle: public CPolygon { 
    public: 
    int area() { return (width * height); } 
    ~CRectangle(){} 
    }; 
+0

はい、新しいバージョンが正しいです。派生クラスの関数を必要ではないにもかかわらず仮想として再宣言するのは良い形式と考えられますが、これは、派生クラスを見たいだけの人々は、関数が仮想であることをまだ知っているようにするためです。 – Omnifarious

+0

あなたは 'class CRectangle:public CPolygon { public: virtual int area {return(width * height);}を意味します。 } }; '? – qazwsx

+0

はい。そして 'virtual〜CRectangle(){}'も同様です。私が言ったように、これらの機能がバーチャルであることを再現することは、単なる良い形であり、言語によっては決して要求されません。 – Omnifarious

答えて

64

クラスに仮想メソッドがある場合は、他のクラスに仮想メソッドを継承させることを意味します。これらのクラスは、ベースクラス参照またはポインタによって破壊される可能性がありますが、これはベースクラスに仮想デストラクタがある場合にのみ機能します。ポリモーフィックに使用できるクラスがある場合、そのクラスも多態的に削除可能でなければなりません。

この質問に対する回答は、hereです。

#include <iostream> 

class FooBase { 
public: 
    ~FooBase() { std::cout << "Destructor of FooBase" << std::endl; } 
}; 

class Foo : public FooBase { 
public: 
    ~Foo() { std::cout << "Destructor of Foo" << std::endl; } 
}; 

class BarBase { 
public: 
    virtual ~BarBase() { std::cout << "Destructor of BarBase" << std::endl; } 
}; 

class Bar : public BarBase { 
public: 
    ~Bar() { std::cout << "Destructor of Bar" << std::endl; } 
}; 

int main() { 
    FooBase * foo = new Foo; 
    delete foo; // deletes only FooBase-part of Foo-object; 

    BarBase * bar = new Bar; 
    delete bar; // deletes complete object 
} 

出力::delete bar;delete foo;だけ~FooBaseを呼び出している間、呼ばれるように、両方のデストラクタ、~Bar~BarBaseを引き起こすこと

Destructor of FooBase 
Destructor of Bar 
Destructor of BarBase 

注以下は、効果を発揮する完全なプログラム例です。後者はさらにundefined behaviorなので、その効果は保証されません。

+0

この回答はスライスの悪影響を実証した例で大幅に改善されるでしょう。それがあるとき、それは私からupvoteを得るでしょう。 – Omnifarious

+1

@Omnifarious:例を追加しました。 –

+0

ちょうど明白です: 'delete foo'は未定義の動作を呼び出します。'〜FooBase'だけを実行することは保証されていません。 – Mankarse

12

(コンセプトと自分自身を解明するために答えを与えるためにしよう)。

struct Foo { 
    virtual ~Foo() {} 
    virtual void bar() = 0; 
}; 

それをオフのまま は通常、 は未定義の動作を引き起こす可能性があるvalgrindのようなツールでメモリリークとして表示されます。

+0

サブクラスオブジェクトをベースクラスの参照またはポインタで削除するのはUBのみです。 –

+3

それをオフにすることは、それ自体では未定義の動作ではありません。定義されていない唯一の振る舞いは、派生型の動的に割り当てられたオブジェクトが 'delete'式で割り当て解除された場合です。ここで、オペランドの型は、基本クラスに仮想デストラクタを持たない基本クラスへのポインタです。クラスに保護された非仮想デストラクタを与えるなど、安全な使用を促すための他のオプションがあります。 –

+0

私が聞いたことから、あなた(または誰か)がその構造体を拡張することに決めた場合、主に問題につながります。 –

1

それは単にCPolygonが削除上の多型ではないので

CPolygon* p = new CRectangle; 
delete p; 

...または任意のスマートポインタに何ラッピングのようなコードは、 は基本的に正常に動作しません、とCRectange部分がないことを意味します適切に破壊された。

CRectangleとCPolygonポリモルフィカリを削除しない場合は、この警告は意味がありません。

+2

しかし、CRectangleとCPolygonの多態性を削除しない場合は、コンパイル時に基本クラスのデストラクタを保護する必要があります。 –

+0

@マークB:必要はありません:CPolygonが抽象的ではない場合(私はOPの抽象度がどれくらい深いかわかりません)、CRectとCPolygonの両方が、参照によってCPolygonアルゴリズムに参加する、真の法定法人です。領域は多型で計算する必要がありますが、ポリモーフィックな破壊は必要ありません。 CPolygon自体は破壊可能でなければなりません(保護されたデストラクタはありません)。 仮想デストラクタを持たないクラスを派生させることは、すべてのメソッド仮想を持たないクラスを派生させることと同じです。仮想ではないものが実質的に動作することを単に期待しないでください。 –

+0

@EmilioGaravaglia:通常、具体的なクラスからの派生を避けることをお勧めします。それはあまりにも多くの異なる装いの意図しないスライスのために開いている。 –

関連する問題