簡體   English   中英

來自QMap的函數指針

[英]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 ++在這里更具表現力。

  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< ,它在右側采用了各種類型,並且要利用這一點,密鑰的類型(此處為: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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM