2011-01-02 10 views
1

私は配置新しいコールは通常、デストラクタへの明示的な呼び出しと一致することを理解します。私の質問は:デストラクタ(デストラクタを持たないメンバ変数を入れずにデストラクタを必要としない場合)は、明示的なデストラクタ呼び出しを安全にスキップできますか?C++では、各プレースメントごとにデストラクタコールが必要ですか?

私の使用例は次のとおりです。C APIのC++バインディングを記述したいと思います。 C APIでは、多くのオブジェクトはポインタでしかアクセスできません。単一のポインタを含むラッパーオブジェクトを作成する代わりに(無駄で意味的に混乱します)。 は、Cオブジェクトのアドレスにオブジェクトを構築するために、配置newを使用します。 C++オブジェクトは、そのコンストラクタまたはデストラクタでは何も行いません。そのメソッドは何もしませんが、Cのメソッドに委譲します。 C++オブジェクトには仮想メソッドは含まれません。

私はこの質問に2つの部分があります。

  1. 実際の生産コンパイラでこの考えがうまくいかない理由はありますか?

  2. これは技術的にC++言語仕様に違反していますか?

+0

あなたは実際にコードでデストラクタを実際に呼び出すことを意味しますか?あなたは(ほとんど)決してそれをするべきではありません。 – Falmarri

+3

新しいプレースメントは、あなたがそうしているまれな時の1つです。 –

+2

@ジョシュ:私は問題を理解しているとは思わない。なぜCオブジェクトをC++オブジェクトの唯一のデータメンバとしていないのでしょうか?次に、メンバー関数で、そのメンバーへのポインターを、委任先のC API関数に渡します。 –

答えて

2

。配置、新しいそこにオブジェクトを、あなたは、単にそれらをmemcpyをすることができますする必要はありませんし、あなたがC++オブジェクトを初期化したいです既存のオブジェクトの「上に」同じレイアウト。

CppObject* cppobject = new (cobject) CppObject; 

古いオブジェクトに対してデストラクタを呼び出していないと何も問題はありませんが - これはリソース漏れなどの問題を引き起こすかどうかは完全にダウンし、古いオブジェクトの種類にあり、ユーザーコードの問題ではない言語であります適合性の問題 - 新しいオブジェクトにメモリを再利用するということは、古いオブジェクトにアクセスできなくなったことを意味します。

operator newの配置フォームは、指定されたアドレスだけを返さなければなりませんが、コンストラクタ(ある場合)が呼び出される前に、新しいオブジェクト自体のメモリを消去しません。 C++言語の規則に従って初期化されていない新しいオブジェクトのメンバーには、一度メモリに保存されていた古いオブジェクトの内容を再利用することとまったく同じ意味ではない、不特定の内容があります。

私があなたを正しく理解している場合、何をしようとしているのかは保証されません。

+0

スタンダードでは、ctorの前にメモリを拭いて新しい配置を停止します。「意図的に他の操作を実行しません」 [18.4.1.3p3、C++ 03]私がそれを誤解していない限り。 –

+0

@Fred Nurk:いいえ、それは ':: operator new'の配置形式の要件で、' :: operator new'を明示的に呼び出して検証できます。新しい式は新しいオブジェクトを初期化します。適切な 'operator new'を呼び出した後でオブジェクトを初期化してメモリをリセットする前に、あるいはオブジェクトの一部が「初期化されていない」まま残っていると、これらの部分にジャンク値を埋め込む前に、コンパイラがマジックを挿入するのを止めるものは何もありません。 –

0

あなたの主要な質問に答えるために、はい、あなたはまだ他のものが起こらなければならないので、デストラクタを呼び出す必要があります。

実際の生産コンパイラでこの考えがうまくいかない理由はありますか?

あなたのC++オブジェクトがCオブジェクトのサイズに収まっていることを確かめてください。

これは技術的にC++言語仕様に違反していますか?

いいえ、スペックになるものはすべてあなたが望むように動作するわけではありません。

+1

なぜデストラクタコールが必要ですか? –

+1

@Charles:コンストラクタが今何もしなくても。他の誰かが構造体を変更して、そのdestructoreを呼び出す必要があるメンバーを含むように変更した場合(つまり、PODでない場合)、あなたは一塊の問題のために自分自身を開いています。今作成したコードは、ソフトウェアの将来のバージョン(特に複数の場所にコードが配布されている場合)に対応していない現在の実装の成果物に依存してはいけません。 –

+2

@Martin York:ノーマ・ロバーツは、それが一般的に「悪い考え」であることを否定しているわけではありません。 –

2

あなたのクラスがPODの場合(それは、con/destructorで何もしない、仮想メンバー関数を持たない、静的でないデータメンバーがないそれらのもの)、コンストラクタまたはをデストラクタと呼ぶ必要はありません。その寿命は、基本となるメモリの寿命にすぎません。構造体がCで使用されているのと同じ方法で使用することができ、構築されているかどうかにかかわらずメンバー関数を呼び出すことができます。

+0

実際には、オブジェクトがPODかどうかは実際問題ではありません。オブジェクトの存続期間は、そのストレージが再利用されるときに終了します。デストラクタを呼び出さなくてもリソースリークやその他の悪いことが起こるかどうかは、まったくユーザコードの問題です。 –

+1

@Charles:質問者が望むことに加えて、彼のクラスがPODの場合、新しいプレースメントも必要ないことを指摘しようとしていました。 –

+0

デフォルト以外のコンストラクタを使用してオブジェクトを初期化する必要がある場合を除き、これはしばしば便利です。 –

1

新しい配置の目的は、オブジェクトプールを作成したり、複数のオブジェクトをstd :: vectorのように連続したメモリ空間に配置することです。

オブジェクトがC構造体の場合、これを行うには新しい配置は必要ありません。sizeof(struct Foo)に基づいてメモリを割り当てるCメソッドを使用することができます。ここで、Fooは構造体名です。アライメントの境界まで複数のオブジェクトを割り当てる必要があります。

は、しかし、私はあなたがメモリ内のCのオブジェクトを持って正しくあなたの質問を理解していれば

0

私は配置新しい呼び出しは、通常、デストラクタへの明示的な呼び出しと一致することを理解します。デストラクタ(コードを記述する必要がなく、デストラクタを持つメンバ変数)が必要ない場合、明示的なデストラクタ呼び出しを安全にスキップできますか?

はい。この回答を投稿する前にニューヨークに飛ぶ必要がない場合は、安全に旅行をスキップできますか? :)しかし、デストラクタが何もしないので本当に不要な場合は、それを呼び出す際にどんな害がありますか?

コンパイラがデストラクタがノーオペレーションでなければならないことがわかった場合、私はその呼び出しを排除することを期待しています。明示的なdtorを書いていないなら、あなたのクラスはまだdtorを持っていることを覚えておいてください。ここで興味深いのは、言語が些細なものかどうかです。

解決策:dtorがノーオペレーションであると思われる場合でも、それらの上に構築する前に、以前に構築されたオブジェクトを破壊してください。

C API用のC++バインディングを書きたいと思います。 C APIでは、多くのオブジェクトはポインタでしかアクセスできません。単一のポインタを含むラッパーオブジェクトを作成する代わりに(無駄で意味的に混乱します)。私はCオブジェクトのアドレスにオブジェクトを構築するために新しい配置を使用したいと思います。

これはレイアウト互換クラスとreinterpret_castの目的です。短い正気確認のために、静的なアサート(例えば、ブーストのマクロ、0x static_assertなど)とサイズやアラインメントをチェックしてください。しかし、最終的には実装がクラスをどのようにレイアウトするかを知る必要があります。ほとんどの場合、必要に応じてプラグマ(または他の実装固有のメカニズム)を使用してこれを制御します。

// in C header 
typedef struct { 
    int n; 
    //... 
} C_A; 
C_A* C_get_a(); 

// your C++ 
struct A { 
    void blah(int n) { 
    _data.num += n; 
    } 

    // convenience functions 
    static A* from(C_A *p) { 
    return reinterpret_cast<A*>(p); 
    } 
    static A const* from(C_A const *p) { 
    return reinterpret_cast<A const*>(p); 
    } 

private: 
    C_A _data; // the only data member 
}; 

void example() { 
    A *p = A::from(C_get_a()); 
    p->blah(42); 
} 

私はむしろ全体reinterpret_castsをstrewingよりも、カプセル化されたこのような変換を維持したい、より:

最も簡単な方法は、C++型内でCの構造体を含むことがあります(つまり、constとnon-constのcall-siteを比較する)、したがって便利な機能です。このタイプの使用がまだサポートされている必要があることに気づかずに、クラスを変更するのはもう少し難しいです。

正確なクラスによっては、データメンバーを公開することもできます。

0

最初の質問は、キャストを使用してみませんか?それでは、配置を新たにすることに何の問題もなく、デストラクタの使用に失敗しないことは明らかです。結果は、CおよびC++型がレイアウト互換である場合に機能します。

第2の質問は、ポイントは何ですか?仮想関数がない場合、コンストラクタやデストラクタを使用していない場合、C++クラスはC型を使用するだけの利点を提供しているようには見えません。

私が想像できる唯一の利点は、C表現を非表示にしたい場合に、すべてのプライベートメンバーを持つクラスにCオブジェクトをオーバーレイし、アクセスのためにメソッドを使用することです。それはあなたの目的ですか? [それは私が考えるべき合理的なことです]

関連する問題