1

私はそのようないくつかのコードを持っています:

#include <string>

class another_foo
{
public:
    another_foo(std::string str)
    {
        // something
    }

private:
    // something
};

class foo
{
public:
    foo();

private:
    another_foo obj;
};

foo::foo() : obj(str) // no `: obj("abcde")`, because it is not that simple in real situation.
{
    std::string str = "abcde"; // generate a string using some method. Not that simple in real situation.
    // do something
}

objのプライベートメンバーであるを初期化しますfooしかし、このコードはコンパイルされません。初期化リストのコンストラクターの本体で変数を使用するにはどうすればよいですか?

私の知る限り、唯一の方法はstr、コンストラクターから生成されたコードを別の関数として分離し、その関数を初期化リストで直接呼び出すことです。あれは...

#include <string>

class another_foo
{
public:
    another_foo(std::string str)
    {
        // something
    }

private:
    // something
};

class foo
{
public:
    foo();

private:
    another_foo obj;

    // std::string generate_str() // add this
    static std::string generate_str() // EDIT: add `static` to avoid using an invalid member
    {
        return "abcde"; // generate a string using some method. Not that simple in real situation.
    }
};

foo::foo() : obj(generate_str()) // Change here
{
    // do something
}

しかし、もっと良い方法はありますか?

4

4 に答える 4

2

しかし、もっと良い方法はありますか?

要するに:いいえ、objの割り当て方法またはセマンティックのいずれかを変更する気がない場合は、そうではありません。

ラムダを使用して、generate_str() を静的にするか、(コードが短い場合) より良いものにするなど、これの変形を行うことができます。

foo::foo() : obj( []{ return "abcde"; }() )
{
}

ただし、オブジェクトの構築に他のメンバーに依存するロジックが必要な場合は、メンバーの初期化順序が相互依存関係を反映していることを確認する必要があります (独立したメンバーから依存するメンバーへの宣言でそれらを順序付けます)。 obj の OR ヒープに割り当てます。

割り当てを変更すると、構築/破壊にコストがかかり、アクセスに非常にわずかなコストがかかるため、ほとんどの場合、これは最善の解決策ではありませんが、問題は解決します:

class foo
{
public:
    foo();

private:
    std::unique_ptr<another_foo> obj; // no sharing of the instance

};

foo::foo() // obj is null
{
    // do something
    auto result_obj_info = compute_something();
    obj = new another_foo( result_obj_info ); 
    // OR if you use this http://stackoverflow.com/questions/12547983/is-there-a-way-to-write-make-unique-in-vs2012
    obj = std::make_unique<another_foo>( result_obj_info );
}

ただし、値がセマンティックになるように、代わりに another_fooのセマンティックを変更することをお勧めします。

#include <string>

class another_foo
{
public:

    another_foo(); // this create an invalid anoter_foo, unusable.

    another_foo(std::string str) // this create a valid another_foo
    {
        // something
    }

    // make sure copy/move functions are available, either automatically or manually

    bool is_valid() const;

private:
    // something
};

inline bool is_valid( const another_foo& obj ) { return obj.is_valid(); }

class foo
{
public:
    foo();

private:
    another_foo obj;
};

foo::foo()
{
    assert( is_valid( obj ) == false);
    std::string str = "abcde"; // generate a string using some method. Not just simple like that in real situation.
    // do something
    obj = another_foo( str );
    assert( is_valid( obj ) == true);
}

そうすれば、あなたの another_foo タイプはそのリソースのハンドルのように機能します。コピーしない場合は、移動専用にします。たとえば、 std::thread または std::unique_ptr がどのように機能するかを見てください。

于 2013-06-20T12:09:24.073 に答える
1

構築中に呼び出されるメンバー関数を作成する際のリスクは、呼び出し時に初期化されていない可能性があるクラスのデータ メンバーの一部を使用するために後で変更する可能性があることです。

次のように、文字列を生成する外部関数を定義することをお勧めします。

namespace {
std::string generate_str()
{
    return "abcde"; 
}
}

foo::foo() : obj(generate_str()) 
{
    // do something
}

そうすれば、関数にパラメーターを渡す必要がある場合、初期化されていないデータ メンバーまたは仮想メンバー関数の戻り値の使用がコンストラクターから見えるようになるため、より簡単にキャッチできます。

于 2013-06-20T12:04:53.960 に答える