4

Empty次のようなコードを使用して内部状態をクリアするメソッドを C++ オブジェクトに追加することがよくあります。

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        *this = Foo();
    }
};

これは、コンストラクターでコードを複製するよりも優れているようです*this = Foo()が、オブジェクトをクリアしたい場合に一般的なアプローチであるかどうか疑問に思いました。これが私を裏側で噛むのを待っていることに何か問題はありますか? この種のことを達成するための他のより良い方法はありますか?

4

10 に答える 10

11

代わりに、コンストラクターに関数を呼び出させます。

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo()
    {
        Reset();
    }

    void Reset()
    {
        n_ = 1234;
        str_ = "Hello, world!";
    }
};

はい、最初に文字列を空の文字列として不必要に初期化し、次に割り当てを行っていますが、これははるかに明確です。

于 2009-01-12T17:42:06.993 に答える
9

潜在的な問題?*これが本当に Foo であることをどうやって知ることができますか?

于 2009-01-12T18:02:29.637 に答える
5

また、オブジェクトを不変にすることを検討してください。つまり、構築されたオブジェクトは変更できません。これにより、多くのシナリオで、予期しない副作用から身を守ることができます。

于 2009-01-12T17:56:33.987 に答える
3

配置の使用を検討してnewください:

void Empty() {
    this->~Foo();
    new (this) Foo();
}

あなたのコードはoperator =、あらゆる種類の副作用につながる可能性のある呼び出しを呼び出します。

コメントに応じて編集します。– このコードは明確に定義されており、標準では明示的に許可されています。時間があれば、その段落を後で投稿する予定です。についてdelete- もちろん。私が意味したのは~Foo()、これは見落としだったということです。はい、ロブも正しいです。ここでは、文字列のデストラクタを呼び出すために、実際にはオブジェクトを破棄する必要があります。

于 2009-01-12T17:56:55.263 に答える
2

コンストラクターに動的に割り当てられたメモリがある場合、これはメモリ リークの潜在的な原因となる可能性があります。

于 2009-01-12T23:39:12.890 に答える
2

これが私がそれを行う方法です:

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);
}

swap 関数を Foo クラスと同じ名前空間に配置します。ユーザーが 2 つの Foo をスワップするために swap を呼び出すと、引数依存のルックアップがそれを見つけます。operator=swap 関数を使用して実装することもできます。

于 2009-01-13T13:58:00.533 に答える
1

これはコンストラクターでコードを複製するよりも優れているようですが、オブジェクトをクリアしたい場合、 *this = Foo() が一般的なアプローチであるかどうか疑問に思いました。

オブジェクトをクリアすることはそれほど一般的ではありません。より一般的には、オブジェクト (おそらく不変オブジェクトでさえも) がインスタンス化されて実際のデータを含んでいるか、インスタンス化されていないかのいずれかです。

リセットされる最も一般的な種類のものコンテナーです... しかし、今は独自のコンテナー クラスを作成していないでしょう。

これが私を裏側で噛むのを待っていることに何か問題はありますか?

はい:

  • これが実際には aFooではなく、代わりに a である場合DerivedFoo
  • Fooの代入演算子が存在しない場合、またはバグがある場合 (たとえば、データ メンバーがネイキッド ポインターであるため、定義されておらず、既定の演算子が適切でない場合) 。

この種のことを達成するための他のより良い方法はありますか?

はい、おそらく無料の関数の方が良いでしょう(上記の問題の両方を回避できます):

template<class T> void reconstruct(T* p)
{
    p->~T();
    new (p) T();
}
于 2009-01-12T19:52:26.577 に答える
-2

はい、これはパフォーマンスの点で効率的ではありません (その場で作業するのではなく、別の foo オブジェクトを作成します)。また、厄介なメモリ リークでコンストラクタにメモリを割り当てると、問題が発生します。

メモリの観点から安全にするためには、this->delete と this = new foo() を呼び出しますが、これは遅くなります。

超高速にしたい場合は、静的な空のオブジェクト フィールドを作成し、Reset で memcpy します。

高速にしたい場合は、プロパティを 1 つずつ割り当てます。

重複せずに合理的なスタイルを維持したい場合は、Ates Goral が提案したように Ctor から Reset を呼び出しますが、デフォルトのパラメーターを使用した高速な構築は失われます。

于 2009-01-12T17:57:58.007 に答える