my_class& operator=(const my_class& rhs)
{
struct{ char data[sizeof(*this)]; } tmp;
new(&tmp) my_class(rhs); //создаем новый временный экземпляр класса в буфере tmp.this->~my_class();//вызываем деструктор на текущем экземпляре.
memcpy(this, &tmp, sizeof(*this));//бинарно копируем временный экземпляр в текущий.return *this;
}
Скажу сразу: класс безопасен для бинарного копирования — т.е. он не содержит ссылок и указателей на самого себя.
По exception-safety все тоже прокатывает на ура — единственное место где может вылезти исключение — это конструктор копирования — в этом случае недоделанный временный экземпляр будет автоматически прибит а текущий экземпляр сохранит свое сотояние.
Но... Гложут смутные сомнения: все так просто и прозрачно, но я нигде еще не видел такого подхода.
Значит, ДОЛЖНЫ быть грабли. Вопрос ГДЕ?
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Re: Вопрос к профи.
От:
Аноним
Дата:
21.01.03 14:44
Оценка:
А если деструктор виртуален и у тебя есть производные от твоего класса?
Здравствуйте, Аноним, Вы писали:
А>А если деструктор виртуален и у тебя есть производные от твоего класса?
1) виртуальный деструктор он и в африке виртуальный.
2) оператор привсаивания не наследуется.
3) :down: :down: :down: (догадайся почему)
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте, TepMuHyc, Вы писали:
TMH>Вот... Сотворил такой assignment.
Сам придумал ?
my_class& operator=(const my_class& rhs)
{
struct{ char data[sizeof(*this)]; } tmp;
new(&tmp) my_class(rhs); //создаем новый временный экземпляр класса в буфере tmp.this->~my_class();//вызываем деструктор на текущем экземпляре.
memcpy(this, &tmp, sizeof(*this));//бинарно копируем временный экземпляр в текущий.return *this;
}
TMH>Значит, ДОЛЖНЫ быть грабли. Вопрос ГДЕ?
В классах (структурах), в которых sizeof не возвращает реальный размер объекта. Примером являются всякие динамически создаваемые конструкции, которые держат свои данные за последним официальным байтом объекта. То есть содержат оператор new типа такого:
Здравствуйте, TepMuHyc, Вы писали:
TMH>ДОЛЖНЫ быть грабли. Вопрос ГДЕ?
TMH>
TMH>my_class& operator=(const my_class& rhs)
TMH>{
TMH> struct{ char data[sizeof(*this)]; } tmp;
TMH> new(&tmp) my_class(rhs); //создаем новый временный экземпляр класса в буфере tmp.
TMH> this->~my_class();//вызываем деструктор на текущем экземпляре.
TMH> memcpy(this, &tmp, sizeof(*this));//бинарно копируем временный экземпляр в текущий.
TMH>
^ Здесь: 1) стандарт гарантирует возможность memcpy в массив char только для POD-типов; 2) alignment requirements my_class в массиве char, определенном "на стеке", могут не соблюдаться. Undefined behavior.
TMH>
TMH> return *this;
TMH>}
TMH>
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[3]: Вопрос к профи.
От:
Аноним
Дата:
21.01.03 14:55
Оценка:
Ты сам
А если в операторе производного класса
Derived & operator = (const Derived & rhs){
if(this != rhs){
Base::operator = (rhs);
//something else
}
return *this;
}
TMH>>Значит, ДОЛЖНЫ быть грабли. Вопрос ГДЕ? КД>В классах (структурах), в которых sizeof не возвращает реальный размер объекта. КД>Я прав?
И да и нет :-)
Вот почему: такие обьекты не работают как "нормальные обьекты" — т.е. не могут создаваться на стеке
и не могут быть созданы через "нормальный" оператор new
Из-за этого, как правило, конструктор у них не-public (чтобы кто-нить) не создал их
по ошибке как "нормальный" обьект. А создаются такие обьекты через функции-фабрики.
Кстати, как написать "классический" оператор присваивания для таких обьектов — лично я не имею понятия :-)
А вообще, грабли вполне конкретные. Спасибо.
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте, TepMuHyc, Вы писали:
TMH>>>Значит, ДОЛЖНЫ быть грабли. Вопрос ГДЕ? КД>>В классах (структурах), в которых sizeof не возвращает реальный размер объекта.
TMH>Кстати, как написать "классический" оператор присваивания для таких обьектов — лично я не имею понятия
Я тоже не знаю. Как присвоить маленький объект большому, вроде понятно. Наоборот — нет
Кстати, под запрет бинарного копирования попадают объекты со счетчиком внешних ссылок. Вспомнил вот.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
.. TMH>Но... Гложут смутные сомнения: все так просто и прозрачно, но я нигде еще не видел такого подхода.
Видел подход наоборот — создаётся нормальный оператор присваивания, а конструктор копирования определяется через этот оператор (хотя, как я понимаю, тебе понравилась идея: дескруктор себе->конструктор себе->бинарная копия).
Borland OWL:
//
// Make a copy of the context entry.
//
THelpContext::THelpContext(const THelpContext& other)
{
*this = other;
}
//
// Make a copy of the context entry.
//
THelpContext&
THelpContext::operator =(const THelpContext& other)
{
HelpFileContextId = other.GetHelpFileContextId();
...
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>1) стандарт гарантирует возможность memcpy в массив char только для POD-типов;
Совершенно согласен — memcpy ересь есмь :-).
Но практически — если данные класса имеют ссылок на другие данные класса — все работает.
Причем, практически на всех компиляторах.
Кстати, на comp.lang.c++.moderated подобные вещи (с оговорками, конечно) появляются довольно часто.
Последний раз видел это дело от Александреску.
ПК>2) alignment requirements my_class в массиве char, определенном "на стеке", могут не соблюдаться.
А как по поводу алайнмента структуры (читай ПОД-а) на стеке? Я ж буфер определяю как
"struct{ char data[sizeof(*this)]; } tmp;".
Или алайнмент для ПОДов и не-ПОДов разный ?
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте, TepMuHyc, Вы писали:
ПК>>2) alignment requirements my_class в массиве char, определенном "на стеке", могут не соблюдаться.
TMH>А как по поводу алайнмента структуры <...> Или алайнмент для ПОДов и не-ПОДов разный ?
Alignment requirements для двух разных классов (POD/non-POD — не важно) могут различаться.
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, TepMuHyc, Вы писали:
TMH>По exception-safety все тоже прокатывает на ура — единственное место где может вылезти исключение — это конструктор копирования — в этом случае недоделанный временный экземпляр будет автоматически прибит а текущий экземпляр сохранит свое сотояние.
Намного безопаснее пользоваться идиомой обмена с временным объектом, а не побитовым копированием. Метод Swap() гарантировано не бросает исключений. Получаем строгую гарантию безопасности исключений:
Здравствуйте, VVV, Вы писали:
VVV>Видел подход наоборот — создаётся нормальный оператор присваивания, а конструктор копирования определяется через этот оператор (хотя, как я понимаю, тебе понравилась идея: дескруктор себе->конструктор себе->бинарная копия).
Вот почему мне не нравится такой подход: Конструктор создает обьект на пустом месте.
А оператор присваивания обязан корректно уничтожить предыдущее состояние обьекта и скопировать новое — что несколько сложнее.
Поэтому, конструктор копирования перед вызовом оператора присваивания обязан выполнить инициализацию обьекта — по сути дела вызвать конструктор по-умолчанию. Иначе оператор присваивания с треском навернется.
Налицо никому не нужная работа.
____________________
God obviously didn't debug, hasn't done any maintenance, and no documentation can be found. Truly amateur work.
Здравствуйте, TepMuHyc, Вы писали:
TMH>Но... Гложут смутные сомнения: все так просто и прозрачно, но я нигде еще не видел такого подхода. TMH>Значит, ДОЛЖНЫ быть грабли. Вопрос ГДЕ?
Это антиидиома. Граблей здесь намного больше чем преимуществ.
Problem #1: It Can Slice Objects
Problem #2: It's Not Exception-Safe
Problem #3: It's Inefficient for Assignment
Problem #4: It Changes Normal Object Lifetimes
Problem #5: It Can Still Break Derived Classes
Problem #6: It Relies on Unreliable Pointer Comparisons (но этого у тебя нет)
Что ты за бред городишь? Могу еще один дельный совет из разряда твоих идей насчет оператора присваивания дать, столь же мощное решение:
ты с конструктором копий не парься, а сразу пиши
*this = rhs;