2016-10-19 10 views
4

は(意図的に不自然な例は以下の)2つのクラスが互いにデフォルトに構成又は構築することができる仮定のシナリオを検討が、いずれかの方法は、高価であると考えられている:コンストラクタの初期化リストは、

struct PrivateKey; 

struct PublicKey { 
    PublicKey(); // generate a random public key (1 minute) 
    PublicKey(const PrivateKey& b); // find a public key corresponding to a private key (1 year) 
    ...members... 
}; 

struct PrivateKey { 
    PrivateKey(); // generate a random private key (1 minute) 
    PrivateKey(const PublicKey& a); // find a private key corresponding to a public key (1 year) 
    ...members... 
}; 

(これはもちろん1つのクラスに凝縮することができますが、質問の妥当性は影響を受けません。一貫性のために、一方と他方の間に対称性がないとしましょう)

ここでは両方のインスタンスとニーズを保持する構造体がありますこのクロス初期化。しかし、両方向が必要な場合があるので、イニシャライザーリストでは実際には切り取ることができません(リストされた順序ではなく、メンバーが定義された順序で実行されます)。

struct X { 
    PublicKey a; 
    PrivateKey b; 
    X(int): a(), b(a) { } 
    X(float): b(), a(b) { } // UB: a(b) happens before b is initialized 
}; 
もちろん

私は試みることができる:

struct X { 
    PublicKey a; 
    PrivateKey b; 
    X(int): a(), b(a) { } 
    X(float): a(), b() { a = PublicKey(b); } 
}; 

をしかし、これははすぐに結果だけをスローするXの第二のコンストラクタでPublicKeyの高価なデフォルト・建設を実行しているの複数の問題を、持っているだけで最初のものです。 副作用PublicKey::PublicKey()からがあるかもしれません。どちらも、まだ一部だけダミー状態でクラスを置く友人Xにさらさ安いプライベートコンストラクタを作成することによって軽減することができますが、いくつかの参照または定数のメンバーで投げるとクラスが割り当て可能-移動したりをスワップ可能な禁止されない場合がありますX::X(float)の体のバリエーション。それに続く良いパターンがありますか?

+3

'PublicKey * a'と' PrivateKey * b'を使って、それらをコンストラクタ本体 '{...}'の内部に構築します。 – AJNeufeld

+3

@AJNeufeld答えは下にあります。 – NathanOliver

+0

クリスタルクリア。私は何を考えていたの?回答を投稿するためのケア? –

答えて

7

工事発注問題ではなく、直接含まれるクラスを埋め込む、含まれるクラスへのポインタを使用して、コンストラクタの本体内に含まれるオブジェクトを自分で構築することで回避できます。

struct X { 
    std::unique_ptr<PublicKey> a; 
    std::unique_ptr<PrivateKey> b; 
    X(int) { 
    a = std::make_unique<PublicKey>(); 
    b = std::make_unique<PrivateKey>(*a); 
    } 
    X(float) { 
    b = std::make_unique<PrivateKey>(); 
    a = std::make_unique<PublicKey>(*b); 
    } 
}; 
+1

おそらく、たとえそのコードが自明であっても、将来のコードに役立つ可能性があるため、コードには説明が必要です。 –

+1

実際には、生ポインタの代わりに 'std :: unique_ptr'を使うべきだと言えるでしょう。 – Angew

+0

'unique_ptr'を使用しているので、コンストラクタ本体で' make_unique'を使用する必要があります。 – NathanOliver

3

クラスは、少なくとも移動、構成可能な場合は、あなたがこれを行うことができるようになります。

struct KeyPair 
{ 
    PublicKey a; 
    PrivateKey b; 
    KeyPair(std::pair<PublicKey, PrivateKey> &&data) : 
    a(std::move(data.first)), 
    b(std::move(data.second)) 
    {} 
}; 

std::pair<PublicKey, PrivateKey> computePublicFirst() 
{ 
    PublicKey a; 
    PrivateKey b(a); 
    return {std::move(a), std::move(b)}; 
} 

std::pair<PublicKey, PrivateKey> computePrivateFirst() 
{ 
    PrivateKey b; 
    PublicKey a(b); 
    return {std::move(a), std::move(b)}; 
} 

struct X 
{ 
    KeyPair keys; 
    X(int) : keys(computePublicFirst()) {} 
    X(float) : keys(computePrivateFirst()) {} 
}; 

ませ動きの割り当ては構成のみを移動し、起こりません。

+0

私は両方の解決策を受け入れることができれば幸いです。私はその優雅さと意味の明確さのために他の答えを選びましたが、あなたが重要であると考えることが多いように、AまたはBの各メソッドコールで余分なポインタ解決を取り除きたいなら、あなたは完璧です。 –

関連する問題