Я не уверен, точен ли заголовок вопроса... Позвольте мне запуститься путем объяснения моего исходного простого сценария и затем идти дальше для объяснения, что было бы я любить делать, но не может.
Первоначально, у меня было что-то как:
class Operand;
Operand genOperandA() { ...; return Operand(); }
Operand genOperandB() { ...; return Operand(); }
... // more operand-generation functions
typedef Operand (*OpGen)();
// Table of function pointers
static const OpGen generators[] =
{
genOperandA,
genOperandB,
...
};
// Function to do some operation on the operand
void operate(Operand& op);
...
// Example call
operate(generators[1]());
Пока неплохо (я думаю). Однако существует теперь несколько полученных типов операнда, например. class RegisterOperand : public Operand
. Я имею новый, выделенный genOperand
функции, которые идеально возвратили бы экземпляры производных типов. Но я не могу сделать этого:
Operand genOperandC() { ...; return RegisterOperand(); }
и я не могу сделать этого:
RegisterOperand genOperandC() { ...; return RegisterOperand(); }
static const OpGen generators[] =
{
...
genOperandC,
};
Однако я знаю, что это работало бы, если бы я должен был возвратить ссылочные типы или типы указателей, таким образом, единственная опция, которую я в настоящее время имею, является чем-то как:
Operand *genOperandC() { ...; return new RegisterOperand(); }
который теперь требует явной очистки, которая не была необходима первоначально.
Какие-либо альтернативы я не рассмотрел?
Могут быть и другие конструкции, которые не требуют использования указателей, но если вам нужно или вы хотите пойти по этому пути, это может вас заинтересовать.
Если возврат указателя является проблемой (из-за необходимости «навести порядок»), вам определенно следует рассмотреть возможность использования интеллектуальных указателей в качестве возвращаемого типа.
Вот пример вашего фабричного метода с интеллектуальными указателями:
boost::shared_ptr<Operand> genOperandC()
{
return boost::shared_ptr<Operand>(new RegisterOperand());
}
Таким образом, вам не придется вручную вызывать delete
: это будет делать деструктор boost: : shared_ptr
для вас, когда потребуется.
Если впоследствии вам нужно преобразовать результирующий указатель, boost
также предоставляет функции преобразования:
boost::shared_ptr<Operand> op = genOperandC();
boost::shared_ptr<RegisterOperand> rop =
boost::dynamic_pointer_cast<RegisterOperand>(op);
Вы можете обернуть:
class Operand
{
public:
private:
std::unique_ptr<OperandImpl> mImpl;
};
Это похоже на шаблон стратегии: фактическое поведение операнда скрыто и доступно через невиртуальный интерфейс. Пользователь получает копию операнда
, ему не нужно ничего знать о его внутреннем устройстве и он может его использовать, а вы можете свободно реализовывать различные производные поведения.