26

オブジェクトをリセットしたい。次のようにできますか?

anObject->~AnObject();
anObject = new(anObject) AnObject();
// edit: this is not allowed: anObject->AnObject();

このコードは明らかに、新しい配置で割り当てられたオブジェクトの典型的なライフサイクルのサブセットです。

AnObject* anObject = malloc(sizeof(AnObject));
anObject = new (anObject) AnObject(); // My step 2.
// ...
anObject->~AnObject(); // My step 1.
free(anObject)
// EDIT: The fact I used malloc instead of new doesn't carry any meaning

変更されたのは、コンストラクタとデストラクタの呼び出しの順序だけです。

では、なぜ次のFAQ にすべての脅威が表示されるのでしょうか。

[11.9]しかし、オブジェクトにnewを割り当てた場合、デストラクタを明示的に呼び出すことはできますか?

FAQ:オブジェクトが新しい配置で割り当てられていない限り、できません。newによって作成されたオブジェクトは削除する必要があります。これにより、2つのことが行われます(覚えておいてください)。デストラクタを呼び出してから、メモリを解放します。

FQA:変換:削除は、デストラクタを明示的に呼び出す方法ですが、メモリの割り当てを解除する方法でもあります。メモリの割り当てを解除せずにデストラクタを呼び出すこともできます。ほとんどの場合、醜くて役に立たないですが、それは可能です。

デストラクタ/コンストラクタの呼び出しは、明らかに通常のC++コードです。コードで使用される保証は、配置の新しい保証から直接生じます。それは標準の核心であり、堅実なものです。どうしてそれを「汚い」と呼び、信頼できないものとして提示することができますか?

newのインプレースメントと非インプレースメントの実装が異なる可能性があると思いますか?私はいくつかの病気の可能性について考えています。たとえば、通常のnewは、ブロックの前に割り当てられたメモリブロックのサイズを置くことができますが、インプレースメントのnewは明らかに(メモリを割り当てないため)行いません。これにより、いくつかの問題のギャップが生じる可能性があります...そのようなnew()の実装は可能ですか?

4

10 に答える 10

31

FQAトロールに吸い込まれないでください。いつものように、彼は事実を間違えます。

新しい配置で作成されているかどうかに関係なく、すべてのオブジェクトについて、デストラクタを直接呼び出すことができます。醜いのは見る人の目にあり、実際に必要になることはめったにありませんが、唯一の難しい事実は、メモリ割り当てとオブジェクト作成の両方のバランスをとる必要があるということです。

「通常の」新規/削除は、メモリ割り当てとオブジェクト作成を結び付けることでこれを少し単純化し、スタック割り当ては両方を行うことでさらに単純化します。

ただし、以下は完全に合法です。

int foo() {
    CBar bar;
    (&bar)->~CBar();
    new (&bar) CBar(42);
 }

両方のオブジェクトが破棄され、スタックメモリも自動的にリサイクルされます。ただし、FQAの主張とは異なり、デストラクタの最初の呼び出しの前に新しい配置が配置されることはありません。

于 2009-07-14T11:18:30.690 に答える
21

Clear()メソッドを実装してみませんか。これは、デストラクタの本体にあるコードが実行することをすべて実行します。次に、デストラクタはClear()を呼び出すだけで、オブジェクトに対して直接Clear()を呼び出して「リセット」します。

クラスが割り当てを正しくサポートしていると仮定した場合の別のオプション:

MyClass a;
...
a = MyClass();

スタックアダプタは明確な機能を提供しないため、このパターンを使用してstd::stackインスタンスをリセットします。

于 2009-07-14T10:53:56.013 に答える
13

技術的には、コンストラクタまたはデストラクタを明示的に呼び出すことはお勧めできません。

deleteキーワードは、使用時にそれらを呼び出すことになります。コンストラクターの新規についても同じことが言えます。

申し訳ありませんが、そのコードで髪を引き裂きたくなります。あなたはこのようにそれをするべきです:

オブジェクトの新しいインスタンスを割り当てます

AnObject* anObject = new AnObject();

オブジェクトのインスタンスを削除します

delete anObject;

これは絶対に行わないでください。

anObject->~AnObject(); // My step 1.
free(anObject)

オブジェクトを「リセット」する必要がある場合は、内部のすべてのインスタンス変数をクリアするメソッドを作成するか、オブジェクトを削除して新しいオブジェクトを割り当てることをお勧めします。

「それは言語の中核ですか?」

それは何の意味もありません。Perlには、forループを作成するための約6つの方法があります。サポートされているために言語で物事を行うことができるという理由だけで、それらを使用する必要があることを意味します。言語の「コア」がそれらをサポートしているので、私はswitchステートメントに使用してすべてのコードを書くことができました。それは良い考えにはなりません。

明らかに必要がないのに、なぜmallocを使用しているのですか。MallocはCメソッドです。

NewとDeleteはC++の友達です

オブジェクトの「リセット」

myObject.Reset();

どうぞ。このようにして、危険な方法でメモリを不必要に割り当てたり、割り当てを解除したりする必要がなくなります。クラス内のすべてのオブジェクトの値をクリアするReset()メソッドを記述します。

于 2009-07-14T10:49:38.563 に答える
9

指定された方法でコンストラクターを呼び出すことはできません。代わりに、placement-newを使用してこれを行うことができます(コードも示しているように):

new (anObject) AnObject();

このコードは、メモリの場所がまだ使用可能な場合、明確に定義されていることが保証されています。

(これが議論の余地のあるコードであるかどうかについての部分を削除しました-それは明確に定義されています。終止符。)

ちなみに、ブロックは正しいです。実装がdelete修正されていない方法–これは、デストラクタの後に。を呼び出すことと同じではありませんfreenewとの呼び出しを常にペアにdeleteし、一方を他方と混合しないでください。これは未定義です。

于 2009-07-14T10:49:14.290 に答える
6

mallocそれらは使用されておらず、free使用されていることに注意してoperator newくださいoperator delete。また、コードとは異なり、newを使用することで、例外安全性が保証されます。ほぼ同等のコードは次のようになります。

AnObject* anObject = ::operator new(sizeof(AnObject));
try
{
    anObject = new (anObject) AnObject();
}
catch (...)
{
    ::operator delete(anObject);
    throw;
}

anObject->~AnObject();
::operator delete(anObject)

提案しているリセットは有効ですが、慣用的なものではありません。正しく理解することは困難であり、そのため、一般的に眉をひそめ、落胆します。

于 2009-07-14T10:50:49.247 に答える
6

はい、あなたがしていることはほとんどの場合有効です。[basic.life] p8によると:

オブジェクトの存続期間が終了した後、オブジェクトが占有していたストレージが再利用または解放される前に、元のオブジェクトが占有していたストレージの場所に新しいオブジェクトが作成された場合、元のオブジェクトを指すポインター、元のオブジェクトを参照するか、元のオブジェクトの名前が自動的に新しいオブジェクトを参照し、新しいオブジェクトの存続期間が開始されると、次の場合に新しいオブジェクトを操作するために使用できます。

  • 新しいオブジェクトのストレージは、元のオブジェクトが占めていたストレージの場所と正確に重なっています。

  • 新しいオブジェクトは元のオブジェクトと同じタイプであり(最上位のcv-qualifiersを無視します)、

  • 元のオブジェクトの型はconst修飾されておらず、クラス型の場合、型がconst修飾または参照型である非静的データメンバーを含まず、

  • 元のオブジェクトも新しいオブジェクトも、重複する可能性のあるサブオブジェクト([intro.object])ではありません。

constしたがって、または参照メンバーがいない場合は合法です。

この保証がない場合std::launder、新しいオブジェクトを使用する場合は、(とにかく実行しているように)新しい配置によって返されるポインターを使用または使用する必要があります。

// no const/ref members
anObject->~AnObject(); // destroy object
new (anObject) AnObject(); // create new object in same storage, ok

anObject->f(); // ok

// const/ref members
anObject->~AnObject();
auto newObject = new (anObject) AnObject();

anObject->f(); // UB
newObject->f(); // ok
std::launder(anObject)->f(); // ok
于 2018-05-07T19:28:00.913 に答える
4

operator =()を使用してリセットしてみませんか?これは議論の余地がなく、はるかに読みやすいです。

A a;
//do something that changes the state of a
a = A(); // reset the thing
于 2018-05-08T08:04:35.583 に答える
3

このようにコンストラクターを呼び出すことはできませんが、メモリーを削除または解放(割り当て解除)しない限り、メモリーを再利用して配置を新規に呼び出すことに問題はありません。このようにオブジェクトをリセットするのは少し大雑把です。明示的にリセットできるオブジェクトを作成するか、swapメソッドを作成し、それを使用してリセットします。

例えば

anObject.swap( AnObject() ); // swap with "clean" object
于 2009-07-14T10:54:40.363 に答える
3

オブジェクトに適切な割り当てセマンティクス(および正しいoperator =)がある場合、* anObject = AnObject()の方が理にかなっており、理解しやすくなります。

于 2009-07-14T10:56:30.203 に答える
1

newの配置で遊ぶよりも、Reset()メソッドのようなものをオブジェクトに追加する方がはるかに優れています。

オブジェクトが割り当てられる場所を制御できるようにすることを目的とした配置の新機能を利用しています。これは通常、ハードウェアにフラッシュチップのような「特別な」メモリがある場合にのみ問題になります。フラッシュチップにいくつかのオブジェクトを配置したい場合は、この手法を使用できます。デストラクタを明示的に呼び出すことができる理由は、YOUがメモリを制御しているため、C++コンパイラは削除の割り当て解除部分を実行する方法を認識していないためです。

リセットメソッドを使用すると、コードをあまり節約できません。メンバーを開始値に設定する必要があります。malloc()はそれを行わないので、とにかくコンストラクターでそのコードを記述する必要があります。メンバーを開始値に設定する関数を作成し、Reset()を呼び出すだけで、コンストラクターやその他の必要な場所から呼び出すことができます。

于 2009-07-14T11:00:23.847 に答える