Showing posts with label factories. Show all posts
Showing posts with label factories. Show all posts

Friday, December 7, 2007

С++ фабрики и вопросы экологии

Итак, очередной выпуск журнала "Кодим Вместе".
Предлагаю немного отвлечься от насущных вопросов, и заняться чем-нибудь более медитативным и расслабляющим. Например, всем вместе реализовать известный дивный программистский паттерн - фабрику.
На С++, конечно же, на чем же еще?

Итак, наш первый вариант:
//------------------------------------------------------------------
[Фабрика I] Вариант первый - брутальный

struct MegaClass
{
};
class SomeFactory
{
public:
MegaClass * CreateSmth()
{
return new MegaClass();
}
};
_Winnie C++ Colorizer
Тоже ничего себе способ, довольно часто встречается, особенно среди любителей managed сред. Недостатки способа очевидны -
1. Можно легко допустить, чтобы кто-то прострелил себе ногу -

{
factory.CreateSmth();
} // утечка на пустом месте
Особенно если метод называется CreateMyMegaSuperObjectAndDoTenAnotherActions, принимает N параметров, и, кроме этого, еще делает M неочевидных действий;

2. Нечитабельно - факт передачи владения неочевиден.
//------------------------------------------------------------------
[Фабрик II] Вариант на "мягкую" четверку

#include <memory>
struct MegaClass
{
};
class SomeFactory
{
public:
std::auto_ptr<megaclass> CreateSmth()
{
return std::auto_ptr<megaclass>(new MegaClass()); // он
}
};
Почему на четверку? Ибо налицо факт явного использования расширения C++. Стандарт C++ утверждает, что временный объект пользовательского типа представляет собой modifyable rvalue. В терминах стандарта, modifyable rvalue - это такая специальная сущность, у которой можно вызывать не константные методы, но которую никак нельзя связывать с не константными ссылками.
Т.е:

// пусть есть некоторое fnc
void fnc(A & a) { /* определенно что-то делаем тут*/}

// тогда, для него
A().fnc(); // так делать можно
fnc(A()); // а так - не везде :)
Несложно заметить, что в этом варианте как раз и имеется попытка связывания временного обекта

std::auto_ptr<megaclass>(new MegaClass())

с не константной ссылкой - аргументом оператора присваивания (или конструктора копирования) класса std::auto_ptr

template<class>
auto_ptr(auto_ptr<_other>&amp; _Right);
auto_ptr<_ty>&amp; operator=(auto_ptr<_ty>&amp; _Right);

Утешает только то, что в микрософтских компиляторах это расширение не отключается даже по /Za. А вот другие компиляторы вполне могут воспринимать такие творения более агрессивно:

/*
"ComeauTest.c", line 11: error: class "std::auto_ptr" has no suitable
copy constructor
return std::auto_ptr(new MegaClass());
^
*/

Посему - минус бал за непереносимость.

//------------------------------------------------------------------
[Фабрика III] Вариант - на пятерку

#include <memory>
struct MegaClass
{
};
class SomeFactory
{
public:
void CreateSmth(std::auto_ptr<MegaClass> * pResult)
{
pResult->reset(new MegaClass());
}
};

Этот вариант лишен всех вышеописанных недостатков.
Делайте поменьше вредных выбросов.