私はgoogletestとgooglemockを使用していくつかのテストユニットを書いていますが、C++ 11スマートポインターとポリモーフィズムに関連する問題で立ち往生しています。
次のクラスがあるとします。
class A {
public:
virtual void doThings() {...};
};
class B {
private:
B(std::unique_ptr<A> a): a_(std::move(a)) {}
std::unique_ptr<A> a_;
};
クラスB
をテストしたいので、A
のモックを作成し、コンストラクターに渡します。
class MockA: public A {
public:
virtual ~MockA() {}
MOCK_METHOD0(doThings, void());
};
TEST(...) {
auto ma = std::make_unique<MockA>();
B b(std::move(ma));
// Set expectation on mock
// call B method
}
問題はこれです:
B
インスタンス内に移動されたため、ma
がnullであるため、期待値検証で例外がスローされます。それを解決するための私の最初の試みは、次のようにB
を変更することでした:
class B {
private:
B(std::unique_ptr<A>& a): a_(a) {}
std::unique_ptr<A>& a_;
};
現在、B
は一意のポインタreferencesを処理し、std::move
を使用しません。テストも変更されました(std::move
の使用はなくなりました):
TEST(...) {
auto ma = std::make_unique<MockA>();
B b(ma);
// Set expectation on mock
// call B method
}
(エラーを正しく理解している場合)参照が多態的ではないため、コードはコンパイルされません(MockA
の一意のポインターをA
の一意のポインターにキャストできません)。
スマートポインターに関する基本的な情報が不足していますか?または、これは一意のポインタで予想される動作なので、クラスを再考する必要があります(共有ポインタを使用している可能性があります)。
最も簡単な方法は、クラスBを以前とまったく同じに保つことですが、テストケースで「b」に渡す前に「ma」からポインタを取得します。そのようです…
TEST(...){
auto ma = std::make_unique<MockA>();
//this will be valid even after the current unique_ptr transfers ownership
//It will be invalidated, however, if a smart pointer deletes it
auto ( or MockA*) rawPtr = ma.get();
B b(ma);
//ma now points to NULL, but rawptr is still valid
//Run analysis on mock via rawPtr
}
このような方法でBの署名を変更することは賢明ではありません。クラスにポインターの排他的所有権を持たせたい場合は、そのポインター用に独自のunique_ptrが必要です。 Bのunique_ptrを参照に変更すると、ポインターを所有するように動作するようにクラスを記述したとしても、Bは技術的にポインターを所有しなくなります。これは、いくつかの本当に奇妙な動作/クラッシュにつながる可能性があります。
BとAの両方がポインターの所有権を持つことを期待する場合、つまり、Bと別のクラス/関数が生きている/実行している間、ポインターが有効である必要がある場合は、shared_ptrを使用する必要があります。ポインターが有効である必要があるのは、Bだけが生きている間だけである場合は、unique_ptrを使用してBのメンバーにする必要があります。
コードの残りの部分を確認しないと、確実に言うことはできませんが、この場合は、unique_ptrをBのメンバーとして使用し続けたいと確信しています。
[〜#〜]編集[〜#〜]
Calethが引き出したように、unique_ptrを持つBの全体的な目的は、ポインタの所有権を持つことです。したがって、Bがptrを削除するときだと判断した場合、そのメモリは無効になり、rawPtrを介してそれにアクセスしようとすると、非常に悪いことが起こります(未定義の動作、クラッシュなど)。
これを行うのは、Bがunique_ptrを処理する方法を知っている場合のみです。 Bがptrを削除するか、unique_ptrがスコープ外になるまで、rawPtrにのみアクセスしてください。
そもそも Ryanの答え は正当です。これは、モックがホワイトボックステスト環境で使用されているためです。つまり、プログラマーはすべてのソースコードを見ることができ、スマートポインターの所有権に違反する方法を正確に知っています。コードが正しく機能することを保証します。
2つのオブジェクトが "Dead drop"配置 でデータを共有する必要がある場合、次の図は設定を示しています。
「spymaster」は、すべてを制御する単体テストコードです。 「情報提供者」(「デッドドロップ」)のインスタンスは、std::shared_ptr
として作成されます。この情報提供者はsetterメソッドを持ち、これは(裏切り者)(データ受信用)から呼び出され、getterメソッドデータを「スパイマスター」が取得できるようにします。
「スパイマスター」は「裏切り者」をstd::unique_ptr
としてインスタンス化し、「無知」(テスト中のコード)に渡します。いったん引き渡されると、「スパイマスター」は「裏切り者」に直接アクセスできなくなります。代わりに、テスト対象のコードと対話し、対話が完了すると、「インフォーマント」または「デッドドロップ」からデータを取得します。