この質問は、コピーとポインターのポリモーフィズムに関するものです。以下のコードを検討してください。通常のオブジェクトであるBaseとDerivedの2 つのクラスがあります。次に、唯一のメンバーとしてBaseへのポインターを持つクラスFooを取得します。
Fooの典型的な使用法は、関数で説明されていmain
ます。への入力Foo::SetMemberX
は、一時オブジェクトである場合とそうでない場合があります。
問題は、渡されたオブジェクトの適切なコピーを作成し、そのアドレスをtoFoo::SetMember
として割り当てたいことです。Base*
Foo::mMember
私は4つの可能な解決策を考え出すことができましたが、どれも私には非常にエレガントに見えません. 最初の 3 つは、以下のコードのFoo::SetMember1
、Foo::SetMember2
、およびに示されていますFoo::SetMember3
。4 番目のオプションは、メモリの割り当てをユーザーに任せることです (例: foo.SetMember(new Derived())
)。これは、明らかなメモリの安全性の問題にはあまり望ましくありません。ユーザーではなく、 Fooがメモリ管理を担当する必要があります。
#include <iostream>
template <typename tBase, typename tPointer>
void ClonePointer(tBase*& destination, const tPointer* pointer)
{
destination = static_cast<tBase*>(new tPointer(*pointer));
}
// Base can be a virtual class
class Base
{
public:
virtual void Function()
{
std::cout << "Base::Function()" << std::endl;
}
virtual Base* Clone() const = 0;
};
class Derived : public Base
{
public:
virtual void Function()
{
std::cout << "Derived::Function()" << std::endl;
}
virtual Base* Clone() const
{
return new Derived(*this);
}
};
class Foo
{
public:
Foo() : mMember(NULL) { }
~Foo()
{
if (mMember != NULL)
{
delete mMember;
mMember = NULL;
}
}
template <typename T>
void SetMember1(const T& t)
{
if (mMember != NULL)
{
delete mMember;
mMember = NULL;
}
ClonePointer(mMember, &t);
}
void SetMember2(const Base& b)
{
mMember = b.Clone();
}
template <typename T>
void SetMember3(const T& t)
{
if (mMember != NULL)
{
delete mMember;
mMember = NULL;
}
mMember = new T(t);
}
Base& GetMember()
{
return *mMember;
}
private:
Base* mMember;
};
int main(int argc, char** argv)
{
{
Foo f1;
Foo f2;
Foo f3;
// The input may or may not be a tempoary/RValue reference
f1.SetMember1(Derived());
f2.SetMember2(Derived());
f3.SetMember3(Derived());
f1.GetMember().Function();
f2.GetMember().Function();
f3.GetMember().Function();
}
// Output:
// Derived::Function();
// Derived::Function();
// Derived::Function();
system("pause"); // for quick testing
}
最初の方法の問題 ( Foo::SetMember1
) は、ランダムで自由なテンプレート関数とテンプレート アクセサがあることです (以下の 3 番目の方法の問題を参照してください)。
2 番目の方法 ( Foo::SetMember2
) の問題は、すべての派生クラスが独自のClone
関数を実装する必要があることです。Baseから派生する多くのクラスが存在するため、これはクラス ユーザーにとって定型コードになりすぎます。何らかの形でこれを自動化するか、実装されたテンプレート関数を使用して基本のCloneableクラスを (各Base派生クラスが明示的に呼び出す必要なしに) 作成できれば、これが理想的なソリューションになります。Clone
3 番目の方法 ( ) の問題は、FooFoo::SetMember3
のテンプレート アクセサーが必要になることです。これは常に可能であるとは限りません。特に、テンプレート以外のクラスでは仮想テンプレート メソッドが許可されていないため ( Fooはテンプレート自体にすることはできません)、必要な機能である可能性があります。
私の質問は次のとおりです。
これらは私が持っている唯一のオプションですか?
私が見逃しているこの問題に対するより優れた、よりエレガントな解決策はありますか?
基本のCloneableクラスを作成し、そこからBaseを派生させて、クローンを自動的に作成する方法はあります
DerivedType::Clone()
か?