2017-12-25 28 views
2

初期化されていないメモリへの参照を渡し、アドレスを取得してからplacement-newを呼び出したり、取得したメモリ位置でデストラクタを呼び出すことはできますか?言い換えると、次のプログラムの法的Cである++またはそれは、未定義の動作をしているん:リファレンスを介してplacement-newとデストラクタを呼び出すことはできますか?

#include <string> 
#include <iostream> 

void ctor (std::string &s) 
{ 
    new (&s) std::string ("Hello, world!"); 
} 

void dtor (std::string &s) 
{ 
    (&s)->std::string::~string(); 
} 

int main() 
{ 
    std::string * memory = static_cast<std::string *> (
    operator new (sizeof (std::string) * 10)); 

    ctor (memory [0]); 

    std::cout << memory [0] << '\n'; 

    dtor (memory [0]); 

    operator delete (memory); 
} 

それはもちろん、動作し、すべてのエラーを生成しなかった、消毒、私はGCCの未定義の動作を試してみました。しかし誰でも標準に基づいて確認/反論することができます。

+0

@CaptainObvliousはい、それは意図ではありませんでした。ありがとう! –

+0

代わりにstatic_castを使用できます。そして、 'operator delete'の引数をキャストする必要はありません。 –

+0

@ M.Mええ、それはよかったです。ありがとう! –

答えて

1

上記のコードは、潜在的なリソースリークある[0]で文字列を、ダブル構築し、二重未定義の動作であり、それを破壊します。配列内のオブジェクトを構築new string[10]

delete[]はオブジェクトを破壊します。新しいオブジェクトを別のオブジェクトの上に作成し、破壊してから、再度破棄します(delete[])。他

何も間違って見えません。私はそれが一般的に悪い習慣である生の割り当てを使用し、コードが例外安全ではない、など

+0

質問は編集されているので、もう 'new []'は使用されていないようです... – cmaster

+0

@Yakkよくある質問は、「リファレンスを使ってプレースメントnewとデストラクタを呼び出せますか?このコードは、できるだけ正確にするためのものです。コードがなくても、私はその質問が十分明確だと感じます。そして、ええ、私は元のコードで恥ずかしい間違いを犯しました。しかたがない。喜んで、私はあまりにもこの種のコードを書くことに慣れていないので、間違いが起こります。しかし、あなたが正しいかもしれない、そして私はこれを新しい質問にする必要があります。しかし、同じタイトルの質問をすることが許可されているかどうかはわかりません。 –

+0

@ヤククだから、同じタイトルの新しい質問を追加することはできず、「他人が時間を投資している」ため、この質問を削除することはできません。私は質問のタイトルがかなり適切であると思う。私はたぶん "through"を "via"と置き換えて、もう一度質問するべきだと思いますか? –

1
dtor (memory [0]); 

delete [] memory; 

は間違いなく未定義の動作であることを意味します。プレースメントnew演算子を使用する場合

ラインに

std::string * memory = new std::string [10]; 

を構築したオブジェクトの有効期間が終了しました。したがって

delete [] memory; 

は未定義の動作です。コード

dtor (memory [0]); 

operator delete (reinterpret_cast<void *> (memory)); 

更新

更新行は行儀されます。

配置new演算子を割り当てられたオブジェクトのデストラクタは一度だけ呼ばれます。

operator newコールによって割り当てられたメモリは、operator deleteコールによって割り当て解除されます。

+1

質問は編集されているようですので、もうdelete []の使用はありません... – cmaster

0

あなたの編集後、もう未定義の動作をするためにそこにいないようです。

しかし、あなたのコードではまだ危険があります:
あなたmemory変数が構築文字列へのポインタの型を持ち、ポインタの初期化後にそのポインタの背後には、文字列がなくても。ポインタはnullptrでもありません。それは危険です。あなたは、何かが正しく構築されたオブジェクトであることをコンパイラにアサーションしています。そのようなものとして、あなたのコンパイラは、構造化されていないオブジェクトを誤って使用する行為であなたを捕まえることはありません。

初期化されていないメモリをchar*変数で追跡し、既に正しく構築されたオブジェクトに対してのみクラス型ポインタを使用することを強くお勧めします。

+0

ありがとう!しかし、これは本当に純粋に言語弁護士の質問ではなく、これが書かなければならないコードかどうかについての質問ではありません。この点で、私は答えがその点を逃していると感じています。なぜこれが定義されていないのか説明できますか?標準を参照できますか? –

+0

「コンパイラへのアサート」について説明できますか? –

+0

@MM「コンパイラにアサーションする」とは、単に「コンパイラを聞いてください。バイトの束は単なる束だと思っていますが、私はプログラマ、あなたの神です。本当に 'std :: string'オブジェクトなので、今は互換性のない型について不平を言ってください!これは、 'std :: string *'へのキャストは、 'std :: string *'型の変数にポインタを格納することと組み合わせて何を行います。 – cmaster

関連する問題