これは古いことは知っていますが、それでもこの問題に対する私の最初のヒットです。これが私がやる方法です。
インターフェイスヘッダーfoo.h:
#pragma once
#include <memory>
enum class Implementations {Simple, Fancy};
class Foo
{
public:
using Ptr = std::unique_ptr<Foo>;
virtual ~Foo() = default;
virtual void do_it() = 0;
};
Foo::Ptr create_foo(Implementations impl); // factory
はい、「プラグマワンス」は厳密に言えば標準ではないことを知っていますが、それは私にとってはうまくいきます。
ここでは何も実装されていないことに注意してください。コンストラクターはありません。抽象クラスをインスタンス化することはできません。ファクトリを介してインターフェイスへのポインタを取得します。仮想関数呼び出しが機能するには、ポインターを介して呼び出す必要があります。仮想デストラクタは、実装へのポリモーフィング以外に特別なことを行う必要がないため、デフォルトになっています。工場は無料の機能です。静的メンバーなどにする必要はありません。これはJavaではありません。
インターフェイスfoo.cpp:
#include "foo.h"
#include "foo_impl.h"
Foo::Ptr create_foo(Implementations impl)
{
switch (impl)
{
case Implementations::Simple:
return std::make_unique<Simple_foo>();
case Implementations::Fancy:
return std::make_unique<Fancy_foo>();
default:
return nullptr;
}
}
ここにファクトリが実装されています。ファクトリは実装を認識している必要があることに注意してください。そのため、インラインで実装しません。インラインの場合、インターフェイスヘッダーには実装ヘッダーを含める必要があり、それを通じて、実装の知識がコールサイトに「リーク」します。
実装ヘッダーfoo_impl.h:
#pragma once
#include "foo.h"
class Simple_foo : public Foo
{
void do_it() override;
};
class Fancy_foo : public Foo
{
void do_it() override;
};
特別なことは何もありません。インターフェースの仮想機能をオーバーライドするだけです。この例は単純なので、両方の実装を同じファイルに入れます。実際のアプリケーションでは異なります。
実装foo_impl.cpp:
#include "foo_impl.h"
#include <iostream>
void Simple_foo::do_it()
{
std::cout << "simple foo\n";
}
void Fancy_foo::do_it()
{
std::cout << "fancy foo\n";
}
関数を実装するだけです。
main.cpp:
#include "foo.h"
int main()
{
auto sf = create_foo(Implementations::Simple);
sf->do_it();
auto ff = create_foo(Implementations::Fancy);
ff->do_it();
return 0;
}
列挙型を介して、必要な実装を選択できます。ポインタは Foo::Ptr
、のエイリアスであるタイプですstd::unique_ptr<Foo>
。コールサイトには実装に関する知識はまったくなく、インターフェイスのみがあります。
出力は期待どおりになります。
simple foo
fancy foo