在C ++中实现Abstract Factory PIMPL Idiom的运行时错误

时间:2015-12-11 18:25:58

标签: c++ pimpl-idiom abstract-factory

当尝试在PIMPL习惯下实现抽象工厂时,我在尝试从Factory范围外获取对象时遇到运行时错误。 (参见"运行时错误"在Main中注释的部分。当从公共类调用acquireInterface()方法时会发生这种情况,该公共类从实现中调用acquireInterface())。 但是,当实现类中的testFactory()函数中的acquireInterface()时,这不会发生(参见" testFactory()"函数)。 有什么建议吗? 在启用了RTTI的MinGW 4.8和VC ++ 11中尝试。

提前致谢。

---文件:IType.hpp ---

#ifndef ITYPE_HPP_
#define ITYPE_HPP_


class ITYPE {
public:
    ITYPE() {
        std::cout << "ITYPE()" << std::endl;
    };
    virtual ~ITYPE(){
        std::cout << "~ITYPE()" << std::endl;
    };
    virtual void hello() = 0;
};


#endif /* ITYPE_HPP_ */

---文件:Factory.hpp ---

#ifndef FACTORY_HPP
#define FACTORY_HPP

#include <memory>
#include <iostream>

#include "IType.hpp"

class Factory {
public:
    Factory();
    ~Factory();

    void testFactory();

    ITYPE* acquireInterface(const char* type);

private:
    class Impl;
    std::unique_ptr<Impl> m_pimpl;
};

#endif //FACTORY_HPP

---文件:FactoryImpl.cpp ---

// Implementations
// ----------------------------------------------------------------------

class ConcreteA : public ITYPE {
public:
    ConcreteA(){ std::cout << "ConcreteA()" << std::endl; }
    ~ConcreteA(){ std::cout << "~ConcreteA()" << std::endl; }

    void hello() { std::cout << "A says Hello" << std::endl; }
};

class ConcreteB : public ITYPE {
public:
    ConcreteB(){ std::cout << "ConcreteB()" << std::endl; }
    ~ConcreteB(){ std::cout << "~ConcreteB()" << std::endl; }
    void hello() { std::cout << "B says Hello" << std::endl; }
};


// ----------------------------------------------------------------------


template<typename Type> ITYPE* createType()
{
    return new Type();
}

/**
 * @brief Abstract Factory for ITYPES.
 */
class Factory::Impl {
    public:
        /**
         * @brief Constructor
         * @details Implementations to be added here using function addType()
         */
        Impl() {
            addType<ConcreteA>("A");
            addType<ConcreteB>("B");
        };

        ITYPE* acquireInterface(const char* type)
        {           
            std::cout << "Acquiring interface for " << type << "..." << std::endl;
            Impl::map_type::iterator iter = m_map.find(type);
            return iter->second();      
        }

    // THIS WORKS
    void testFactory()
    {
        ITYPE* iA = acquireInterface("A");
        iA->hello();
        delete iA;

        ITYPE* iB = acquireInterface("B");
        iB->hello();
        delete iB;
    }

    private:
        /** @brief Adds a type to the Abstract Factory
         *  @param componentName short string (no spaces) to identify implementation */
        template<typename Type>
        void addType(const char* componentName) {
            ComponentFactoryFuncPtr function = createType<Type>;
            m_map.insert(std::make_pair(componentName, function));
        };

public:
        /**
         * @brief Abstract factory constructor function pointer
         */
        typedef  ITYPE* (*ComponentFactoryFuncPtr)();

        /**
         * @brief Type for map holding type identifier / constructor function
         */
        typedef std::map<const char*, ComponentFactoryFuncPtr> map_type;

private:
     map_type m_map;    /**< map holding type identifier / constructor function */
};

Factory::Factory() : m_pimpl(new Impl()) { }

Factory::~Factory() { }

void Factory::testFactory()
{
    m_pimpl->testFactory();
}

ITYPE* Factory::acquireInterface(const char* type)
{
    return m_pimpl->acquireInterface(type);
}

---主要---

#include <iostream>
#include <memory>
using namespace std;

#include "Factory.hpp"

int main() 
{
    {
        Factory f;

        // OK
        std::cout << "This works:"  << std::endl;
        f.testFactory();

        // Runtime error (acquireInterface("A") returns NULL ptr)
        ITYPE* iA = f.acquireInterface("A");
        iA->hello();
        delete iA;

        ITYPE* iB = f.acquireInterface("B");
        iB->hello();
        delete iB;
    }

    return getchar();
}

1 个答案:

答案 0 :(得分:1)

您的代码中有一件坏事:

typedef std::map<const char*, ComponentFactoryFuncPtr> map_type;

主要问题是您无法保证const char*文字具有相同的地址,即使文字是相同的。

您的代码会尝试此操作:

ITYPE* iA = f.acquireInterface("A");

无法保证字符串文字"A"的指针值与您为地图设置的"A"指针值相同。因此,行为未定义将会发生什么。

如果map键的目标是使用字符串,则使用字符串。您现在可以完全控制键所代表的内容,而不是const char *,而您不知道编译器将如何处理字符串文字。你真正知道的是"A"是一个字符串文字,但这是你真正知道的。

修复应该是这样的:

typedef std::map<std::string, ComponentFactoryFuncPtr> map_type;