空の」コンストラクタやデストラクタは、生成されたものと同じことをするのでしょうか?
質問
以下のような(おもちゃの)C++クラスがあるとします。
class Foo {
public:
Foo();
private:
int t;
};
デストラクタが定義されていないため、C++コンパイラは自動的にクラス
Foo
. デストラクタが動的に割り当てられたメモリをクリーンアップする必要がない場合 (つまり、コンパイラが提供するデストラクタに合理的に頼ることができる場合)、空のデストラクタを定義する、すなわち、デストラクタを定義する必要があります。
Foo::~Foo() { }
は、コンパイラが生成したものと同じことをするのでしょうか?空のコンストラクタはどうでしょう -- つまり
Foo::Foo() { }
?
違いがあるとすれば、それはどこに存在するのでしょうか? そうでない場合、どちらかの方法が他よりも好ましいのでしょうか?
どのように解決するのですか?
同じことをする(要するに何もしない)ことになります。しかし、書かないのと同じではありません。なぜなら、デストラクタを書くには、ベースクラスのデストラクタが動作していることが必要だからです。もしベースクラスのデストラクタがprivateであったり、その他の理由で呼び出すことができないのであれば、あなたのプログラムは欠陥があることになります。次のように考えてみてください。
struct A { private: ~A(); };
struct B : A { };
B型(つまり暗黙のうちにA型)のオブジェクトを破壊する必要がない限り、これはOKです。例えば、動的に生成されたオブジェクトに対してdeleteを呼ばない、あるいはそもそもそのオブジェクトを生成しない場合などです。もしそうであれば、コンパイラは適切な診断を表示します。では、明示的に提供する場合
struct A { private: ~A(); };
struct B : A { ~B() { /* ... */ } };
これは暗黙のうちにベースクラスのデストラクタを呼び出そうとするもので、定義時にすでに
~B
.
デストラクタの定義とメンバデストラクタへの暗黙の呼び出しを中心としたもう一つの違いがあります。このスマートポインタのメンバを考えてみましょう。
struct C;
struct A {
auto_ptr<C> a;
A();
};
型のオブジェクトを想定してみましょう。
C
にある A のコンストラクタの定義で作成されます。
.cpp
ファイルの中で作成され、そのファイルには構造体
C
. さて、もしあなたが構造体
A
を使用し
A
オブジェクトの破壊を要求する場合、コンパイラは上のケースと同じように暗黙のうちにデストラクタの定義を提供します。このデストラクタは暗黙のうちに auto_ptr オブジェクトのデストラクタを呼び出します。そしてそれは、それが保持している
C
オブジェクトを指すポインタを削除します。
C
! の中に現れた
.cpp
ファイルに表示され、そこで構造体 A のコンストラクタが定義されています。
これは実は、pimplイディオムを実装する際によくある問題です。ここでの解決策は、デストラクタを追加して、その空の定義を
.cpp
ファイルの中で、構造体
C
が定義されているファイルです。そのメンバのデストラクタを呼び出すときに、構造体の定義がわかります。
C
の定義を知り、そのデストラクタを正しく呼び出すことができます。
struct C;
struct A {
auto_ptr<C> a;
A();
~A(); // defined as ~A() { } in .cpp file, too
};
なお
boost::shared_ptr
はそのような問題はありません。その代わりに、そのコンストラクタが特定の方法で呼び出されたときに完全な型を要求します。
現在のC++で違いが出てくるもう一つの点は、コンストラクタで
memset
などを使いたい場合です。このような型はもはや POD (plain old data) ではないので、ビットコピーすることは許されません。次の C++ バージョンではこの状況が改善され、他のより重要な変更が行われない限り、このような型をまだビットコピーできるようになっています。
コンストラクタについて質問されたので。まあ、これらについても同じようなことが言えます。コンストラクタには、デストラクタへの暗黙の呼び出しも含まれることに注意してください。auto_ptr のようなものでは、これらの呼び出しは (実行時に実際に行われないとしても - 純粋な可能性はすでにここでは重要です) デストラクタと同じ害を及ぼし、コンストラクタ内の何かがスローするときに起こります - コンパイラはその後メンバのデストラクタを呼び出すことが要求されます。 この回答 はデフォルトコンストラクタの暗黙的な定義をいくらか使用しています。
また、上記のデストラクタについて述べた、可視性とPODネスについても同様です。
初期化に関して一つ重要な違いがあります。もしユーザが宣言したコンストラクタを置いた場合、その型はもうメンバの値の初期化を受け取らず、必要な初期化はすべてコンストラクタに任されます。例を挙げます。
struct A {
int a;
};
struct B {
int b;
B() { }
};
この場合、常に次のようになります。
assert(A().a == 0);
以下は未定義の動作ですが、なぜなら
b
が初期化されなかったからです (コンストラクタがそれを省略したのです)。値はゼロかもしれませんが、他の奇妙な値であってもかまいません。このような初期化されていないオブジェクトから読み取ろうとすると、未定義の動作が発生します。
assert(B().b == 0);
これは、この構文を
new
のように
new A()
(最後の括弧に注意してください - もし括弧が省略された場合、値の初期化は行われず、初期化できるユーザが宣言したコンストラクタが存在しないためです。
a
は初期化されないままとなります)。
関連
-
[解決済み】構造体のベクター初期化について
-
[解決済み】C++ - ステートメントがオーバーロードされた関数のアドレスを解決できない。
-
[解決済み】演算子のオーバーロード C++; <<操作のパラメータが多すぎる
-
[解決済み] Java で、あるコンストラクタを別のコンストラクタから呼び出すにはどうすればよいですか?
-
[解決済み] C#でベースコンストラクタを呼び出す
-
[解決済み] あるコンストラクタを別のコンストラクタから呼び出す
-
[解決済み] ベースクラスのコンストラクタを呼び出す際のルールは?
-
[解決済み] React / React Nativeでコンストラクタを使用する場合とgetInitialStateを使用する場合の違いとは何ですか?
-
[解決済み] ベースの仮想デストラクタを明示的に呼び出す必要がありますか?
-
[解決済み】なぜC++コンパイラはoperator==とoperator!=を定義しないのでしょうか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み】C++でランダムな2倍数を生成する
-
[解決済み】C++エラー:の初期化に一致するコンストラクタがありません。
-
[解決済み】Visual Studio 2013および2015でC++コンパイラーエラーC2280「削除された関数を参照しようとした」が発生する
-
[解決済み】エラー:不完全な型へのメンバーアクセス:前方宣言の
-
[解決済み】浮動小数点数の乱数生成
-
[解決済み】標準ライブラリにstd::endlに相当するタブはあるか?
-
[解決済み】C++ - ステートメントがオーバーロードされた関数のアドレスを解決できない。
-
[解決済み] 数値定数の前にunqualified-idを付けて、数値を定義することを期待する。
-
[解決済み】警告 - 符号付き整数式と符号なし整数式の比較