web-dev-qa-db-ja.com

メソッドを呼び出すと、メソッドが呼び出されたオブジェクトが無効になることをクラスのユーザーに明示的に通知する方法は?

このスニペットを考えてみましょう:

_class Foo
{
    int m_fileDescriptor;
public:
    Bar transformIntoBar()
    {
        Bar bar(m_fileDescriptor);
        m_fileDescriptor = -1;
        return bar;
    }
};
_

ご覧のとおり、FooBarに「変換」されると、その_m_fileDescriptor_は_-1_になり、オブジェクト全体が無効になります。

問題は、クラスのユーザーがそれが起こることを伝える方法がないことです。

1つの解決策は、フレンドmake_bar()関数を宣言することです。

_friend Bar make_bar(Foo f);
_

そしてFooを移動のみにします(とにかく発生するはずですが、簡潔にするために例ではスキップしました)。このように、ユーザーは自分のFooオブジェクトが移動され、そのため役に立たなくなるという事実を認識する必要があります。

ただし、このソリューションに対しては、深刻な問題があります。このようなメソッド(たとえばmake_baz(Foo f)など)が増えると、フレンドヘルパー関数のAPI全体を作成する必要がありますが、これは明らかに望ましいことではありません。

私の質問は-私のクラスのユーザーに大声で叫ぶために私ができることはそれを呼び出すと、オブジェクトが役に立たなくなるですか?

6
TuRtoise

コメントでamonが述べたように、関数シグネチャに&&を追加することで、thisを右辺値参照にすることができます。

Bar transformIntoBar() &&
{
    ...
}

このようにして、次のコードはコンパイルに失敗します。

Foo foo;
Bar bar = foo.transformIntoBar();
// compile error (clang):
// 'this' argument to member function 'transformIntoBar' is an lvalue, but function has rvalue ref-qualifier

ただし、以下はコンパイルされます。

Foo foo;
Bar bar = std::move(foo).transformIntoBar();

慣例により、そこから移動されたオブジェクトの多くは「特別な」状態にあるため、C++開発者はfooを使用しないか、ドキュメントを参照してどの状態であるかを知る必要があることを知っていますfooは移動後のものです。

これは、一時的な(名前のない)Fooオブジェクトが既に右辺値であるため、作成される場合にも適切に処理します。

Bar bar = createFoo().transformIntoBar();
9
pschill

特定のケースに関する詳細情報で行うことができます。しかし、ビルダーオブジェクトと流暢なパターンを試すことをお勧めします

Bar b = Builder.CreateFoo() //ISettableFile
    .SetFile() //IConvertable
    .ConvertToBar() // Bar

Foo f = Builder.CreateFoo() //ISettableFile
    .SetFile() //IConvertable
    .ConvertToFoo() // Foo

インターフェイスとプライベートコンストラクターを使用して、基になるオブジェクトまたは変換関数へのアクセスを制限する

1
Ewan

通常、オブジェクトのライフサイクルの制御を維持する必要がある場合は、オブジェクトをユーザーコードに直接渡さないほうがよいでしょう。これは、開閉ロジックを追加で必要とするI/Oに特に当てはまります。

つまり、オブジェクトを返す代わりに、オブジェクトを使用するロジックを提供する必要があります。もちろん、オブジェクトをロジックに提供しますが、オブジェクトには、管理する明確なライフサイクルがあります。

あなたがこれを示したクラスでは遅すぎると思います、あなたは1つ上のレベルの異なるデザインが必要になるでしょう。残念ながら、具体的な例を示すためのC++についてはほとんど知りませんが、コンセプト自体が役立つことを願っています。

0

C++のように見えるので、キャストは右辺値にのみ適用できると考えます。

class Foo {
    ...
    operator Bar() && {
        Bar r(m_fileDescriptor);
        m_fileDescriptor = -1;
        return r;
    }
};
0
Deduplicator