nothrow ctor vs. pimpl
От: Аноним  
Дата: 04.08.05 10:09
Оценка:
Привет,

Хочу написать класс с 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();
}

Спасибо за любые идеи!

Пожалуйста, не забывайте пользоваться тегами [c]...[/c], [code]...[/code] и т.п. для выделения фрагментов кода. -- ПК
Re: nothrow ctor vs. pimpl
От: Glоbus Украина  
Дата: 04.08.05 10:15
Оценка: :))
Здравствуйте, Аноним, Вы писали:

А>Привет,


А>Хочу написать класс с 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.

Ну а с точки упражнения, Ваша реализация будет работь
Re: nothrow ctor vs. pimpl
От: korzhik Россия  
Дата: 04.08.05 10:32
Оценка: 32 (2)
Здравствуйте, Аноним, Вы писали:

А>Привет,


А>Хочу написать класс с 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 impl

  enum {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 assertions
typedef char StaticAssert[sizeof(implBuf_.size) >= sizeof(ClassImpl)];
}

Class::~Class() throw()
{
pimpl_->~ClassImpl();
}
Re[2]: nothrow ctor vs. pimpl
От: Кодт Россия  
Дата: 04.08.05 10:33
Оценка: 1 (1)
Здравствуйте, 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(); }
Перекуём баги на фичи!
Re[2]: nothrow ctor vs. pimpl
От: Bell Россия  
Дата: 04.08.05 10:37
Оценка:
Здравствуйте, Glоbus, Вы писали:

G>Прости, я не совсем понимаю выделенную строку в деструкторе. А почему бы не использовать а) обычный delete для удаления


Потому что используется placement new.

G>б) std::auto_ptr<> как поле класса для хранения указателя, вместо голого указателя?

потому что std::auto_ptr<> в деструкторе вызовет delete, а этого в данном случае делать нельзя.
Любите книгу — источник знаний (с) М.Горький
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.