Хочу написать класс с nothrow конструктором и использовать при этом pimpl-идиому. Может, кто подскажет, как это сделать красиво? Пока получается достаточно коряво. В хедэре пишу:
class ClassImpl;
class Class { // non-polymorphicpublic:
Class() throw();
~Class() throw();
private:
// we had better use BOOST for classes that cannot be copied
Class(const Class& rhs); // no impl
Class& operator=(const Class& rhs);// no implenum {
eAtLeastImplSize = 128
};
char implBuf_[eAtLeastImplSize]; // must be declared BEFORE pimpl_
ClassImpl* pimpl_;
};
В .cpp-файле пишу:
#include <new>
#include"ThisVeryHeader.h"class ClassImpl {/*has a nthrow ctor...*/};
Class::Class() throw()
: pimpl_( new(static_cast<void*>(implBuf_)) ClassImpl )
{
// we had better use BOOST for static assertionstypedef char StaticAssert[sizeof(implBuf_) >= sizeof(ClassImpl)];
}
Class::~Class() throw()
{
pimpl_->~ClassImpl();
}
Спасибо за любые идеи!
Пожалуйста, не забывайте пользоваться тегами [c]...[/c], [code]...[/code] и т.п. для выделения фрагментов кода. -- ПК
Здравствуйте, Аноним, Вы писали:
А>Привет,
А>Хочу написать класс с nothrow конструктором и использовать при этом pimpl-идиому. Может, кто подскажет, как это сделать красиво? Пока получается достаточно коряво. В хедэре пишу:
А>class ClassImpl;
А>class Class { // non-polymorphic
А>public:
А> Class() throw();
А> ~Class() throw();
А>private:
А>// we had better use BOOST for classes that cannot be copied
А> Class(const Class& rhs); // no impl
А> Class& operator=(const Class& rhs);// no impl
А> enum {
А> eAtLeastImplSize = 128
А> };
А> char implBuf_[eAtLeastImplSize]; // must be declared BEFORE pimpl_
А> ClassImpl* pimpl_;
А>};
А>В .cpp-файле пишу:
А>#include <new>
А>#include"ThisVeryHeader.h"
А>class ClassImpl {/*has a nthrow ctor...*/};
А>Class::Class() throw()
А> : pimpl_( new(static_cast<void*>(implBuf_)) ClassImpl )
А>{
А>// we had better use BOOST for static assertions
А> typedef char StaticAssert[sizeof(implBuf_) >= sizeof(ClassImpl)];
А>}
А>Class::~Class() throw()
А>{
А> pimpl_->~ClassImpl();
А>}
Прости, я не совсем понимаю выделенную строку в деструкторе. А почему бы не использовать а) обычный delete для удаления б) std::auto_ptr<> как поле класса для хранения указателя, вместо голого указателя?
А>Спасибо за любые идеи!
Удачи тебе, браток!
Re: nothrow ctor vs. pimpl
От:
Аноним
Дата:
04.08.05 10:27
Оценка:
Здравствуйте, Аноним, Вы писали:
А>Привет,
А>Хочу написать класс с nothrow конструктором и использовать при этом pimpl-идиому. Может, кто подскажет, как это сделать красиво? Пока получается достаточно коряво. В хедэре пишу:
А>class ClassImpl;
А>class Class { // non-polymorphic А>public: А> Class() throw(); А> ~Class() throw();
А>private: А>// we had better use BOOST for classes that cannot be copied А> Class(const Class& rhs); // no impl А> Class& operator=(const Class& rhs);// no impl
А> enum { А> eAtLeastImplSize = 128 А> }; А> char implBuf_[eAtLeastImplSize]; // must be declared BEFORE pimpl_ А> ClassImpl* pimpl_; А>};
А>В .cpp-файле пишу:
А>#include <new> А>#include "ThisVeryHeader.h"
А>class ClassImpl {/*has a nthrow ctor...*/};
А>Class::Class() throw() А> : pimpl_( new(static_cast<void*>(implBuf_)) ClassImpl ) А>{ А>// we had better use BOOST for static assertions А> typedef char StaticAssert[sizeof(implBuf_) >= sizeof(ClassImpl)]; А>}
А>Class::~Class() throw() А>{ А> pimpl_->~ClassImpl(); А>}
А>Спасибо за любые идеи!
Вероятность генерация исключений оператором new для систем общего назначения .пренебрежительно мала.
Можно использовать new(std::nothrow), однако это тоже не даст никаких весомых приимуществ над "обычным" new.
Ну а с точки упражнения, Ваша реализация будет работь
Здравствуйте, Аноним, Вы писали:
А>Привет,
А>Хочу написать класс с nothrow конструктором и использовать при этом pimpl-идиому. Может, кто подскажет, как это сделать красиво? Пока получается достаточно коряво. В хедэре пишу:
[...]
У этого кода есть одна большая проблема — это выравнивание.
Статический массив не обязан иметь корректное выравнивание для хранения ClassImpl. На некоторых платформах можешь получить аппаратное исключение.
Используй boost::aligned_storage и тэги форматирования:
#include"boost/aligned_storage.hpp"class ClassImpl;
class Class
{
public:
Class() throw();
~Class() throw();
private:
Class(const Class& rhs); // no impl
Class& operator=(const Class& rhs);// no implenum {eAtLeastImplSize = 128};
boost::aligned_storage<eAtLeastImplSize> implBuf_; // must be declared BEFORE pimpl_
ClassImpl* pimpl_;
};
В .cpp-файле пишу:
#include <new>
#include"ThisVeryHeader.h"class ClassImpl {/*has a nthrow ctor...*/};
Class::Class() throw()
: pimpl_( new(static_cast<void*>(implBuf_.address())) ClassImpl )
{
// we had better use BOOST for static assertionstypedef char StaticAssert[sizeof(implBuf_.size) >= sizeof(ClassImpl)];
}
Class::~Class() throw()
{
pimpl_->~ClassImpl();
}
Здравствуйте, Glоbus, Вы писали:
G>Прости, я не совсем понимаю выделенную строку в деструкторе. А почему бы не использовать а) обычный delete для удаления б) std::auto_ptr<> как поле класса для хранения указателя, вместо голого указателя?
Может быть, потому что размещение impl'а в куче сопряжено с риском bad_alloc. Поэтому для него отводится место в теле объекта и выполняется placement new.
Кстати говоря, незачем заводить член-указатель. Достаточно было написать метод доступа к буферу
class Impl;
class Class
{
private:
char storage[enough_room];
inline Impl* impl() { return (Impl*)storage; }
inline Impl const* impl() const { return (Impl const*)storage; }
public:
Class() throw();
Class(Class const& src) throw(); // либо писать рукодельный конструктор копирования, либо делать класс некопируемым
Class(Something); // чисто для расширения кругозора
~Class();
Class& operator=(Class const&); // рукодельный либо запрещать
};
/////////////////////////////////////////////////class Impl { ..... };
STATIC_ASSERT(enough_room >= sizeof(Impl));
void Class::Class() { new(impl()) Impl(); }
void Class::Class(Class const& src) { new(impl()) Impl(src.impl()); }
void Class::Class(Something some) { new(impl()) Impl(some); }
void Class::~Class() { impl()->~Impl(); }
Здравствуйте, Glоbus, Вы писали:
G>Прости, я не совсем понимаю выделенную строку в деструкторе. А почему бы не использовать а) обычный delete для удаления
Потому что используется placement new.
G>б) std::auto_ptr<> как поле класса для хранения указателя, вместо голого указателя?
потому что std::auto_ptr<> в деструкторе вызовет delete, а этого в данном случае делать нельзя.