実際に呼び出されるデストラクタに関しては、大きな意味があります。Gotw88、Q3、A3 を確認してください。私はすべてを小さなテストプログラムに入れました(Visual-C++なので、stdafx.hを許してください)
// Gotw88.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
class A
{
protected:
bool m_destroyed;
public:
A() : m_destroyed(false) {}
~A()
{
if (!m_destroyed)
{
std::cout<<"A destroyed"<<std::endl;
m_destroyed=true;
}
}
};
class B : public A
{
public:
~B()
{
if (!m_destroyed)
{
std::cout<<"B destroyed"<<std::endl;
m_destroyed=true;
}
}
};
B CreateB()
{
return B();
}
int _tmain(int argc, _TCHAR* argv[])
{
std::cout<<"Reference"<<std::endl;
{
const A& tmpRef = CreateB();
}
std::cout<<"Value"<<std::endl;
{
A tmpVal = CreateB();
}
return 0;
}
この小さなプログラムの出力は次のとおりです。
Reference
B destroyed
Value
B destroyed
A destroyed
ここで、セットアップの簡単な説明を行います。B は A から派生していますが、どちらにも仮想デストラクタがありません (これが WTF であることはわかっていますが、ここで重要です)。CreateB() は B を値で返します。Main は CreateB を呼び出し、最初にこの呼び出しの結果を型 A の const 参照に格納します。次に、CreateB が呼び出され、結果が型 A の値に格納されます。
結果は興味深いものです。まず、参照によって格納する場合は正しいデストラクタが呼び出され (B)、値によって格納する場合は間違ったデストラクタが呼び出されます。2 つ目 - 参照に格納する場合、デストラクタは 1 回だけ呼び出されます。これは、オブジェクトが 1 つしかないことを意味します。値によって、(異なるデストラクタへの) 2 つの呼び出しが発生します。つまり、2 つのオブジェクトがあることを意味します。
私のアドバイス - const 参照を使用してください。少なくとも Visual C++ では、コピーが少なくなります。使用しているコンパイラが不明な場合は、このテスト プログラムを使用して適合させ、コンパイラをチェックしてください。適応する方法は?コピー/移動コンストラクターとコピー代入演算子を追加します。
クラスAとBのコピーと代入演算子をすぐに追加しました
A(const A& rhs)
{
std::cout<<"A copy constructed"<<std::endl;
}
A& operator=(const A& rhs)
{
std::cout<<"A copy assigned"<<std::endl;
}
(B についても同じです。大文字の A をすべて B に置き換えてください)
これにより、次の出力が得られます。
Reference
A constructed
B constructed
B destroyed
Value
A constructed
B constructed
A copy constructed
B destroyed
A destroyed
これにより、上記の結果が確認されます (B が A から派生しているため、構築された B から構築された A の結果に注意してください。したがって、Bs コンストラクターが呼び出されるたびに As コンストラクターが呼び出されます)。
追加のテスト: Visual C++ は、const 参照と同じ結果 (この例) を持つ非 const 参照も受け入れます。さらに、タイプとして auto を使用すると、(もちろん) 正しいデストラクタが呼び出され、戻り値の最適化が開始され、最終的には const 参照と同じ結果になります (ただし、もちろん、auto には A ではなくタイプ B があります)。 .