Итак, очередной выпуск журнала "Кодим Вместе".
Предлагаю немного отвлечься от насущных вопросов, и заняться чем-нибудь более медитативным и расслабляющим. Например, всем вместе реализовать известный дивный программистский паттерн -
фабрику.
На С++, конечно же, на чем же еще?
Итак, наш первый вариант:
//------------------------------------------------------------------
[Фабрика 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>& _Right); auto_ptr<_ty>& operator=(auto_ptr<_ty>& _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()); } };
|
Этот вариант лишен всех вышеописанных недостатков.
Делайте поменьше вредных выбросов.
3 comments:
Почему плох вариант с возвратом по значению - непонятно. Кстати, посмотри на std::auto_ptr_ref (напрямую его никто не использует), зачем он нужен ;)
И комо-онлайн всё корректно компилирует.
In strict mode, with -tused, Compile succeeded.
Скорее, плох последний вариант. В частности, вопросами "а что будет если туда передать NULL" и меньшей очевидностью из кода "это фабрика". В то время как код
auto_ptr<T> f();
кричит "ЙА ФАБРЕКО!".
А так же он тупо неудобен, при таком стиле программирования часто приходится заводить временные переменные только ради того, что бы передать на них указатель, создание переменной и её инициализация в две строчки.
Кстати, в варианте "на пятерку" становится проблематичным сделать инициализацию как обычно -
class ModestClass
{
int i;
std::auto_ptr<MegaClass> m;
ModestClass(SomeFactory &f)
:i(0)
,m( ??? )
{
}
};
PS.
Tag is not allowed: <pre> - чо ваще за фигня. Не могу нормально отформатировать код.
>в микрософтских компиляторах это расширение не отключается даже по /Za.
/W4:
warning C4239: nonstandard extension used : 'argument' : conversion from 'T' to 'T &'
Ну и #pragma warning(error) по желанию.
1) насчет "ЙА ФАБРИКО" и затруднений инициализации - согласен на 100%
кста, насчет инициализации, можно вынести вызов и в тело конструктора, нефиг мегалогику в списке инициализации городить )
2) насчет возвращаемого значения - Комо и тот поддерживает сие начиная с версии 4.2.45.2.
Я просто хотел подчеркнуть нестандартность этого действа, бывают, знаешь, такие проекты с повышенными требованиями к переносимости
Post a Comment