来自 QMap 的函数指针

Function pointer from QMap

本文关键字:指针 函数 QMap 来自      更新时间:2023-10-16

我正在尝试按照以下示例在我的 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++在这里更具表现力。

  1. 您不需要显式Create/Free方法 - 编译器可以自动生成它们。
  2. 你肯定需要一个虚拟析构函数,否则接口将毫无用处。要从中派生的任何类都必须具有虚拟析构函数,此规则中很少有专门的例外。
  3. 虚拟方法的所有实现都应该声明为override,包括析构函数,但不能virtual,因为这会违反 DRY。
  4. 这些类可以带有它们的名称,以便工厂只需知道它们的类型即可注册它们。这是工厂的可选行为。
#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<,要利用这一点,密钥的类型(此处:名称)必须到达比较器。

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());
}