派生クラスのデストラクタが呼ばれない(clangでコンパイルした場合)
解決済
回答 3
投稿
- 評価
- クリップ 2
- VIEW 2,112
clang でコンパイルすると結果が違う(デストラクタが呼ばれない)
以下のプログラムをコンパイルするとclangとGCCでは結果が違う
具体的にはmain関数内4番目のブロック
{
Base* ap = new Derived[2];
delete[] ap;
}
を、実行した場合
GCCでは、派生クラスのデストラクタとベースクラスのデストラクタが呼ばれる
clangは、ベースクラスのデストラクタだけ呼ばれる(派生クラスのデストラクタは呼ばれない)
この結果の違いはなぜ起こるのでしょうか?
該当のソースコード
#include <iostream>
class Base {
public:
Base() {
std::cout << "Base()" << std::endl;
}
virtual ~Base() {
std::cout << "~Base()" << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived()" << std::endl;
}
~Derived() {
std::cout << "~Derived()" << std::endl;
}
};
int main()
{
std::cout << "{\n Derived a;\n}" << std::endl;
// 1
{
Derived a;
}
std::cout << "------------------------" << std::endl;
std::cout << "{\n Derived ary[2];\n}" << std::endl;
// 2
{
Derived ary[2];
}
std::cout << "------------------------" << std::endl;
std::cout << "{\n Base* p = new Derived;" << std::endl;
std::cout << " delete p;\n}" << std::endl;
// 3
{
Base* p = new Derived;
delete p;
}
std::cout << "------------------------" << std::endl;
std::cout << "{\n Base* ap = new Derived[2];" << std::endl;
std::cout << " delete[] ap;\n}" << std::endl;
// 4
{
Base* ap = new Derived[2];
delete[] ap;
}
}
試したこと
オンラインでコンパイル実行ができるサイトで
https://wandbox.org
http://rextester.com
試してみた結果もclangの場合だけ派生クラスのデストラクタが呼ばれませんでした
GCCとvc++は派生クラスのデストラクタも呼ばれる
補足情報(コンパイラのバージョン)
Apple LLVM version 9.1.0 (clang-902.0.39.2)
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+4
Stack Overflowに同様の質問がありました。
c++ - virtual destructor fail using array - Stack Overflow
どうやら、new[]で作る配列の場合、多態性があるものとして扱われないようです。つまり、virtualがついていようが静的な型(コンパイル時に決定される変数自体の型)であるBaseとして扱って、動的な型(実行時に判別されるオブジェクト自体の型)であるDerivedとしては扱わないということらしいです。このように静的な型と動的な型が一致せず、virtualなデストラクタになっている場合は、未定義な動作となるとのことです。未定義な動作ですので、Baseのデストラクタだけが呼ばれる場合も、Derivedのデストラクタが呼ばれる場合も、もっと言えばエラーで落ちてしまっても、C++としては仕様通りの動作となります。
日本語の文献も見つけたので紹介しておきます。
operator delete, operator delete[] - cppreference.com
クラス固有のオーバーロード
...
削除されるオブジェクトの静的な型がその動的な型と異なる場合 (基底へのポインタを通して多相オブジェクトを削除するときなど)、そしてその静的な型のデストラクタが virtual な場合、単一オブジェクト形式の delete はその仮想デストラクタの最終オーバーライダーの定義地点から解放関数の名前の探索を開始します。 実行時にどの解放関数が実行されるかにかかわらず、コンパイルするためには operator delete の静的に可視なバージョンがアクセス可能でなければなりません。 それ以外の場合では、基底へのポインタを通して配列を削除するとき、または非仮想デストラクタを持つ基底へのポインタを通してアクセスするとき、動作は未定義です。
...
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
単にコンパイルのバグかもしれません。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
Base* ap = new Derived[2];
は operator new[]を呼び出します。
本来、operator new[]は単にサイズを確保するだけで初期化は行われないはずです。
コンパイラによって違うのは、おそらくそれがundefined behaviorであるからです。
おそらく実行したいのは↓
Base **ap = new Base*[2];
ap[0] = new Derived;
ap[1] = new Derived;
delete ap[0];
delete ap[1];
delete[] ap;
よりスマートにしたいのであれば、
std::vector<std::unique_ptr<Base>> v;
v.push_back(std::make_unique<Derived>());
v.push_back(std::make_unique<Derived>());
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.20%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/08/17 08:33
なるほど規格上は未定義の動作になるのか
したがってどちらのコンパイル結果も正しい(実装がそうなっていただけ)
ちょっとスッキリしました。
回答ありがとうございました。