質問
C ++オブジェクトに Empty
メソッドを追加して、次のようなコードを使用して内部状態をクリアすることがよくあります。
class Foo
{
private:
int n_;
std::string str_;
public:
Foo() : n_(1234), str_("Hello, world!")
{
}
void Empty()
{
*this = Foo();
}
};
これは、コンストラクタでコードを複製するよりも優れているようですが、オブジェクトをクリアしたい場合、 * this = Foo()
が一般的なアプローチかどうか疑問に思いましたか?裏側で私を噛むのを待っているのに問題はありますか?この種のことを達成するための他のより良い方法はありますか?
解決
代わりにコンストラクターに関数を呼び出させます:
class Foo
{
private:
int n_;
std::string str_;
public:
Foo()
{
Reset();
}
void Reset()
{
n_ = 1234;
str_ = "Hello, world!";
}
};
はい、まず空の文字列として文字列を不必要に初期化してから割り当てを行いますが、これははるかに明確です。
他のヒント
潜在的な問題? *これが本当にFooであることをどのように知っていますか?
このEmptyメソッドで行うことは、新しく構築されたオブジェクトを変数に手動で割り当てることと本質的に同じです(Empty関数が行うこと)。
個人的に、Emptyメソッドを削除し、そのメソッドのすべての使用を次のように置き換えます。
// let's say, that you have variables foo and pfoo - they are properly initialized.
Foo foo, *pfoo;
// replace line "foo.Empty()" with:
foo = Foo();
// replace line "pfoo->Empty()" with:
delete pfoo;
pfoo = new Foo();
// or
*pfoo = Foo();
このEmptyメソッドを使用しても、実際には利点はありません。それはそれが呼び出された魔女のオブジェクトに実際に何が起こっているかを隠します、名前も最良の選択ではありません。
呼び出し元が本当にきれいなオブジェクトを望んでいる場合-オブジェクトを自分で構築するのに問題はありません。
また、オブジェクトを不変にすることを検討してください。 i.e。、構築された場合、それらは変更できません。これにより、多くのシナリオで予期しない副作用を防ぐことができます。
あなたが提案したものよりもさらに一般的なものがあります。スワップを使用します。
基本的には次のようにします:
T().swap(*this);
多くの標準コンテナ(すべてのSTLコンテナ?)には一定の時間スワップメソッドがあるため、これはコンテナをクリアし、ストレージが解放されていることを確認するための優れたシンプルな方法です。
同様に、「縮小して収まる」良い方法です。コンテナも同様ですが、デフォルトのコンストラクタの代わりにコピーコンストラクタを使用します。
プレースメントの使用を検討 new
:
void Empty() {
this->~Foo();
new (this) Foo();
}
コードは operator =
を呼び出します。これにより、あらゆる種類の副作用が発生する可能性があります。
編集。 –このコードは明確に定義されており、明確に許可されています。時間が見つかったら、後で段落を投稿します。 delete
について–もちろん。私が意味したのは〜Foo()
で、これは見落としでした。はい、ロブも正しいです。ここでは、文字列のデストラクタを呼び出すために実際にオブジェクトを破棄する必要があります。
コンストラクタで動的に割り当てられたメモリがある場合、これはメモリリークの潜在的な原因になる可能性があります。
次のようにします:
class Foo {
private:
int n_;
std::string str_;
public:
Foo() : n_(1234), str_("Hello, world!")
{
}
void Empty()
{
Foo f;
swap(f);
}
void swap(Foo & other) {
std::swap(n_, other.n_);
swap(str_, other.str_);
}
};
void swap(Foo & one, Foo & other) {
one.swap(other);
}
スワップ関数をFooクラスのような同じネームスペースに入れます。引数に依存するルックアップは、ユーザーがswapを呼び出して2つのFooをスワップするときに検索します。スワップ関数を使用して operator =
を実装することもできます。
これは、コンストラクタでコードを複製するよりも優れているようですが、オブジェクトをクリアしたい場合、* this = Foo()が一般的なアプローチかどうか疑問に思いましたか?
オブジェクトの消去は、それほど一般的なことではありません。より一般的には、オブジェクト(おそらく不変のオブジェクトであっても)がインスタンス化され、実際のデータを含むか、インスタンス化されません。
リセットされる最も一般的な種類はコンテナです...しかし、あなたは今、あなた自身のコンテナクラスを書くことはないでしょう、
これを裏側で噛むのを待っているのに何か問題がありますか?
はい:
- これが実際には
Foo
ではなく、DerivedFoo
である場合
-
Foo
の代入演算子が存在しない場合、またはバグがある場合(たとえば、定義されておらず、デフォルトの演算子が適切でない場合、たとえばデータメンバーがネイキッドポインターである場合) )。
この種のことを達成する他のより良い方法はありますか?
はい、おそらく無料の機能の方が良いでしょう(上記の両方の問題を回避します):
template<class T> void reconstruct(T* p)
{
p->~T();
new (p) T();
}
はい、これはパフォーマンスの点で効率的ではありません(所定の場所で作業する代わりに別のfooオブジェクトを作成します)。
メモリの観点から安全にするには、this-&gt; deleteおよびthis = new foo()を呼び出しますが、スローになります。
超高速にしたい場合は、静的な空のオブジェクトフィールドを作成し、リセットでmemcpyします。
プロパティを1つずつ高速に割り当てたい場合。
重複なしで合理的なスタイルを維持したい場合、Ates Goralが示唆したようにCtorからResetを呼び出しますが、デフォルトのパラメーターではより高速な構築を失います。