Ошибка выполнения при реализации абстрактной фабричной идиомы PIMPL в переполнении стека

при попытке реализовать абстрактную фабрику под идиомой PIMPL я получаю ошибку времени выполнения при попытке получить объект за пределами области действия фабрики. (См. Раздел с комментариями к «Ошибка времени выполнения» в Main. Это происходит при вызове метода acquInterface () из открытого класса, который вызывает acquInterface () из реализации).
Однако этого не происходит, когда acquInterface () из функции testFactory () внутри класса реализации (см. Функцию «testFactory ()»).
Любой совет?
Пробовал в MinGW 4.8 и VC ++ 11 с включенным RTTI.

Заранее спасибо.

— Файл: 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();
}

0

Решение

Одна плохая вещь в вашем коде это:

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;
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]