在 C++ 中实现抽象工厂 PIMPL Idiom 的运行时错误
Runtime error implementing Abstract Factory PIMPL Idiom in C++
在 PIMPL 习惯用法下尝试实现抽象工厂时,在尝试从工厂范围外获取对象时出现运行时错误。 (请参阅 Main 中用 "Runtime Error" 注释的部分。当从 public class 调用 acquireInterface() 方法时会发生这种情况,后者从实现中调用 acquireInterface() )。
但是,当实现 class 中的 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();
}
您的代码中的一件坏事是:
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;
在 PIMPL 习惯用法下尝试实现抽象工厂时,在尝试从工厂范围外获取对象时出现运行时错误。 (请参阅 Main 中用 "Runtime Error" 注释的部分。当从 public class 调用 acquireInterface() 方法时会发生这种情况,后者从实现中调用 acquireInterface() )。 但是,当实现 class 中的 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();
}
您的代码中的一件坏事是:
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;