[英]Function pointer from QMap
我正在嘗試根據此示例在我的QT項目中實現工廠方法模式: https : //www.codeproject.com/Articles/363338/Factory-Pattern-in-Cplusplus
#include <QCoreApplication>
#include <QDebug>
class IAnimal
{
public:
virtual int GetNumberOfLegs() const = 0;
virtual void Speak() = 0;
virtual void Free() = 0;
};
typedef IAnimal* (__stdcall *CreateAnimalFn)(void);
// IAnimal implementations
class Cat : public IAnimal
{
public:
int GetNumberOfLegs() const { return 4; }
void Speak() { qDebug() << "Meow" << endl; }
void Free() { delete this; }
static IAnimal * __stdcall Create() { return new Cat(); }
};
class Dog : public IAnimal
{
public:
int GetNumberOfLegs() const { return 4; }
void Speak() { qDebug() << "Woof" << endl; }
void Free() { delete this; }
static IAnimal * __stdcall Create() { return new Dog(); }
};
工廠類別:
// Factory for creating instances of IAnimal
class AnimalFactory
{
private:
AnimalFactory();
AnimalFactory(const AnimalFactory &) { }
AnimalFactory &operator=(const AnimalFactory &) { return *this; }
typedef QMap<QString,CreateAnimalFn> FactoryMap;
FactoryMap m_FactoryMap;
public:
~AnimalFactory() { m_FactoryMap.clear(); }
static AnimalFactory *Get()
{
static AnimalFactory instance;
return &instance;
}
void Register(const QString &animalName, CreateAnimalFn pfnCreate);
IAnimal *CreateAnimal(const QString &animalName);
};
AnimalFactory::AnimalFactory()
{
Register("Cat", &Cat::Create);
Register("Dog", &Dog::Create);
}
void AnimalFactory::Register(const QString &animalName, CreateAnimalFn pfnCreate)
{
m_FactoryMap[animalName] = pfnCreate;
}
IAnimal *AnimalFactory::CreateAnimal(const QString &animalName)
{
FactoryMap::iterator it = m_FactoryMap.find(animalName);
if( it != m_FactoryMap.end() )
return it.value();
return NULL;
}
但是我遇到這樣的錯誤:
cannot convert 'IAnimal* (__attribute__((__stdcall__)) *)()' to 'IAnimal*' in return
return it.value();
只有現有的anwser(將函數指針插入QMap(Qt)中 )建議將Create()函數設為靜態,這似乎無濟於事。
我將非常感謝您的任何建議。
這有點復雜。 您正在編寫C ++,因此不應復制Java。 C ++在這里更具表現力。
Create
/ Free
方法-編譯器可以自動為您生成它們。 override
,包括析構函數,但不應聲明為virtual
因為這違反了DRY。 #include <QtCore>
class IAnimal {
public:
virtual int GetNumberOfLegs() const = 0;
virtual QString Speaks() = 0;
virtual ~IAnimal() {}
};
class Cat : public IAnimal {
public:
int GetNumberOfLegs() const override { return 4; }
QString Speaks() override { return QStringLiteral("Meow"); }
static auto className() { return "Cat"; }
};
class Dog : public IAnimal {
public:
int GetNumberOfLegs() const override { return 4; }
QString Speaks() override { return QStringLiteral("Woof"); }
static auto className() { return "Dog"; }
};
現在我們可以擁有一個通用工廠。 請注意,所有理智的C ++容器類型都會管理其數據。 您無需在銷毀時明確清除它們。 我們正在利用C ++ 11。 Register
方法將僅接受從Interface
派生的類型,並且該方法使用lambda表達式自動生成構造函數。
實例的生存期應通過在main()
實例化來明確控制。
#include <type_traits>
#include <typeindex>
#include <map>
template <class Interface> class Factory {
template <class C, class T = void> struct enable_if_I :
std::enable_if<std::is_base_of<Interface, C>::value, T> {};
using create_fn = Interface* (*)();
std::map<QByteArray, create_fn, std::less<>> m_creators;
std::map<std::type_index, QByteArray> m_names;
static Factory *&instance_ref() { // assume no inline static yet
static Factory *m_instance;
return m_instance;
}
Factory(const Factory &) = delete;
Factory &operator=(const Factory &) = delete;
public:
Factory() {
Q_ASSERT(!instance());
instance_ref() = this;
}
virtual ~Factory() { instance_ref() = {}; }
通常,注冊需要派生類的類型和名稱。 前提是該類是否具有className()
成員。 工廠存儲工廠功能和名稱。 這樣就可以查找名稱,而無需將className
作為接口的虛擬方法。
template <class T> typename enable_if_I<T>::type Register(const QByteArray &name) {
m_creators[name] = +[]()->Interface* { return new T(); };
m_names[{typeid(T)}] = name;
}
當類名已知時,我們可以利用它們來注冊一個或多個類(僅給出它們的類型)。
template <class T1> typename enable_if_I<T1>::type Register() {
this->Register<T1>(T1::className());
}
template <class T1, class T2, class...T> typename enable_if_I<T1>::type Register() {
this->Register<T1>(T1::className());
this->Register<T2, T...>();
}
實例創建方法經過優化,無論格式如何都不會復制給定的名稱。 這就是為什么我們將std::map<K, V, std::less<>>
映射與透明比較器一起使用的原因。 QByteArray
提供了operator<
,它在右側采用了各種類型,並且要利用這一點,密鑰的類型(此處為:name)必須到達比較器。
template <typename T> static Interface *CreateA(T &&t) {
return instance() ? instance()->Create(std::forward<T>(t)) : nullptr;
}
Interface *Create(QLatin1String name) const { return Create(name.data()); }
template <typename T> Interface *Create(T &&name) const;
static const QByteArray &NameOfA(const Interface * obj);
const QByteArray &NameOf(const Interface *) const;
static Factory *instance() { return instance_ref(); }
};
template <class Interface>
template <typename T> Interface *Factory<Interface>::Create(T &&name) const {
auto it = m_creators.find(name);
return (it != m_creators.end()) ? it->second() : nullptr;
}
namespace detail {
const QByteArray & null() { static const QByteArray n; return n; }
}
template <class Interface>
const QByteArray &Factory<Interface>::NameOfA(const Interface *obj) {
return instance() ? instance()->NameOf(obj) : detail::null();
}
template <class Interface>
const QByteArray &Factory<Interface>::NameOf(const Interface *obj) const {
auto it = m_names.find(typeid(*obj));
return (it != m_names.end()) ? it->second : detail::null();
}
泛型工廠采用接口和具體類型,並將它們全部注冊在構造函數中。 這使工廠的建造變得簡單。
template <class Interface, class ...Types>
class GenericFactory : public Factory<Interface> {
public:
GenericFactory() {
this->template Register<Types...>();
}
};
using AnimalFactory = GenericFactory<IAnimal, Cat, Dog>;
使用示例,帶有斷言指示所需的行為。 請注意,要銷毀對象,只需delete
其實例即可。 編譯器將生成調用。
int main() {
Q_ASSERT(!AnimalFactory::instance());
{
AnimalFactory animals;
Q_ASSERT(AnimalFactory::instance());
auto *dog1 = AnimalFactory::CreateA("Dog");
Q_ASSERT(dynamic_cast<Dog*>(dog1));
Q_ASSERT(AnimalFactory::NameOfA(dog1) == Dog::className());
Q_ASSERT(dog1->Speaks() == QStringLiteral("Woof"));
auto *dog2 = AnimalFactory::CreateA(QLatin1String("Dog"));
Q_ASSERT(dynamic_cast<Dog*>(dog2));
auto *cat = AnimalFactory::CreateA("Cat");
Q_ASSERT(dynamic_cast<Cat*>(cat));
Q_ASSERT(cat->Speaks() == QStringLiteral("Meow"));
Q_ASSERT(AnimalFactory::NameOfA(cat) == Cat::className());
delete cat;
delete dog2;
delete dog1;
}
Q_ASSERT(!AnimalFactory::instance());
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.