Здравствуйте, 1234, Вы писали:
1>Если можно, разъясните что можно а что нельзя, и почему нельзя.
Можно "двигать" все. С++ тебе этого не запрещает. (И хорошо!)
Два "но"
1) ты отдаешь себе отчет в том что происходит.
2) "двигаемые" объекты позиционно независимы (что как правило хорошая практика).
Объекты содержащие vtbl тоже прекрасно двигаются если dst корректно выровнено.
К сожалению в базовых коллекциях STL нет семантики move или transfer ownership.
Но по моему сия проблема обсуждалась как раз в working group в последнее время.
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, Павел Кузнецов, Вы писали:
ПК>>Стандарт гарантирует (3.9) возможность побайтного копирования только для POD-типов. В остальных случаях гарантий, что в результате побайтного копирования будет получен эквивалентный или даже валидный объект, нет.
ПК>>Например, можно представить себе (экзотичную, но допустимую с точки зрения стандарта) реализацию виртуальных функций в виде соответствия <адрес объекта> -> <VMT>, которая при перемещении объектов полиморфных классов побайтным копированием работать не будет. Также можно пофантазировать и на тему объектов, хранящихся в памяти не непрерывно...
CS>Т.е. не написано что "memmove" к не POD типам применять нельзя. Правильно?
Нет, не написано. Тем не менее вышепроцитированное именно означает, что "memmove к не POD типам применять нельзя". Точнее, применять то ты его можешь сколько угодно, но вот только ожидать, что таки способом можно скопировать не-POD объект из одного места памяти в другое не приходится.
CS>"Также можно пофантазировать и на тему объектов, хранящихся в памяти не непрерывно..."
CS>Давай рассуждать здраво.... CS>Наличие оператора new placement гарантирует что объект выделится а) непрерывным куском
Непрерывнум куском "сырой" памяти — да, гарантирует.
CS>б) займет sizeof(T).
Нет, разумеется. Ничего такого не гарантируется. Гарантируется, что объект займет не менее чем 'sizeof(T)'. За примерами далеко ходить не надо — динамически выделяемые массивы занимают больше, чем 'sizeof' соответствующего массивного типа.
CS>"<адрес объекта> -> <VMT>" теоретически возможно, но практически не реально совсем. CS>Если так будет то все библиотеки C++ object persistence / memory mapping улетают в трубу.
Почему улетают? Во-первых, совсем не обязательно все. Во-вторых, это говорит только о том, что не существует портабельных библиотек, но запросто могут существовать кросс-платформенные.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>c-smile,
>> АШ> причем здесь STL, перемещение (memmove) и копирование (memcpy) разрешено только для POD-типов и это регулирует стандарт — так что тень на плетень не надо наводить и так ночь на дворе.
>> Где это такое написано? Ссылку даем, да?
ПК>Стандарт гарантирует (3.9) возможность побайтного копирования только для POD-типов. В остальных случаях гарантий, что в результате побайтного копирования будет получен эквивалентный или даже валидный объект, нет.
Но это совсем не значит, что в конкретной реализации для конкретного класса это не будет работать. Просто, будет платформенно-зависимый код.
ПК>Например, можно представить себе (экзотичную, но допустимую с точки зрения стандарта) реализацию виртуальных функций в виде соответствия <адрес объекта> -> <VMT>, которая при перемещении объектов полиморфных классов побайтным копированием работать не будет. Также можно пофантазировать и на тему объектов, хранящихся в памяти не непрерывно...
Нафантазировать можно много чего. Есть всё-таки наиболее распространённые реализации.
1>А можно по подробнее остановится на проблеме копирования таблици виртуальных функций ? 1>Я об это часто слышал, но как-то внятного объяснения невстречал.
Коротко говоря, для хранения класса в памяти компилятор использует приблизительно такую структуру:
struct X {
void *vptable;
void *vftable;
данные
...
};
vptable указывает на таблицу вирутальных предков нашего класса, а vftable указывает на таблицу виртуальных функций. Соответственно отсюда вытекает все то, о чем я говорил. Внутреняя кухня довольно простая, в частности советую попробовать такой код:
class X {
public:
void f() { printf("%p",this); }
};
...
X *x=0; x->f();
Результатом выполнения этой программы будет вывод на экран 0
Соответственно, если сделать так:
class X {
public:
virtual void f() { printf("%p",this); }
};
то при попытке выполнения произойдет ошибка, т.к. программа не сможет найти указатель на таблицу вирутальных функций...
P.S. извиняюсь за небольшое лирическое отступление
Что такое "НЕПОСРЕДСТВЕННЫЙ адрес" и чем оно отличается от просто "адрес"?
И вообще я уже запарился повторять одну и ту же фразу.
Давай этот раз я её выделю особо "КОГДА ОТДАЕШЬ СЕБЕ ОТЧЕТ".
Если же ты используешь в быту virtual inheritnace with virtual base classes,
что в общем и целом разрешено в C++ но в design guides не поощрается,
то как минимум ты обязан себе отдавать отчет в том что происходит.
Для чего-то же ты вставил этот :public virtual base?
Т.е. предполагается что ты уже задумываешься над memory layout, правильно?
А если при том ты хочешь "двигать" такие классы то ты должен знать свой(и) компилятор(ы)
Например для VC++ вот это полезно знать:
For the so-called Microsoft C++ Object Mapping, the object layout
algorithm for a given class is approximately:
0. start with an empty struct;
1. add a virtual function table pointer (vfptr) if this class
has new virtual functions and does not inherit a vfptr from the
non-virtually-inherited parts of some direct non-virtual base class;
2. add a virtual base displacement table pointer (vbptr) if this
class has virtual bases and does not inherit a vbptr from the
non-virtually-inherited parts of some direct non-virtual base class;
3. add an embedded instance of the non-virtually-inherited parts of each
direct non-virtual base class, in declaration order;
4. add the non-static data members in declaration order;
5. add an embedded instance of the non-virtually-inherited parts of each
direct or indirect virtual base class, (if I recall correctly) in the
order found given a depth-first left-to-right preorder traversal of
the base class graph.
(Intentionally avoiding discussion of the details of bitfields, padding
for alignment, virtual function tables, virtual base displacement tables,
"adjuster thunks", 0-sized bases, transitive virtual bases, the dreaded
"construction displacement" mechanism, etc., etc.)
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Уточняем: ПК>
5.3.4/10 A new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array.
Ну а я о чем говорил? (bold emphasizing is mine)
void *someptr;
выражение:
new(someptr) T;
вызовет placement функцию ::operator new с параметрами
::operator new( sizeof(T), someptr )
Возвращаясь же к практическим соображениям и резюмируя вышесказанное (с глубоким почтением ко всем участникам) еще раз рискну заметить:
Не виртуальные классы (т.е. "простые" но уже не POD) "двигать" достаточно безопасно.
Экзепмпляры виртуальных классов "двигать" в принципе можно, но отдавая себе отчет что такое решение потенциально не portable т.е.зависит от конкретного компилятора. Хотя все известные мне компиляторы и safe в этом плане, проверять все равно надо. Условно говоря такое решение — на совести разработчика и мотвационных установок проекта.
И лирика: на самом деле такие вот возможности (не обязательно эти) и делают C++ тем чем он есть — платформой для создания эффективных програм. С++ ффективный и универсальный но не "дуракоустойчивый". Что есть то есть.
Здравствуйте, 1234, Вы писали:
1>Здравствуйте, Bell, Вы писали:
B>>Здравствуйте, 1234, Вы писали:
1>>>Здравствуйте, Анатолий Широков, Вы писали:
1>>>>>memmove, memcpy and e.t.c.
АШ>>>>Таким способом можно переносить объекты только POD-типов. Приведенный вами класс POD-типом не являетс 1>>>Уточню. 1>>>Т.е. вы утверждаете что класс B>>... 1>>>копировать используя memcpy/memmove нельзя ?
B>>Формально — нельзя, т.к., как уже было сказано, он не является POD-типом. И ограничение это накладывается стандартом языка. B>>Практически же копирование с использованием memcpy/memmove для подобных классов во многих случаях (читай на многих платформах) работать будет. Другой вопрос — стОит ли пренебрегать стандартом... 1>Я совершенго согласен — стандарт надо блюсти. 1>Всё дело в том что для меня стоял вопрос оптимизации по времени, в таких случаял ИМХО можно применять решения который могут немного выходить из общепринятых идиом языка, но в тоже время, они должны оставатся безопасными и универсальными. 1>Вот поэтому я и решил посоветоватся — "Можно ли .... ?" а не "Нужно ли.... ?"
1>>>Тогда возникает второй вопрос. (хотя на самом деле для меня именно он был первым). 1>>>Возможно я чего-то непонимаю, но спускаясь по дереву вызовов stl::vetor::insert() 1>>>я попадаю в конце концов в место где вызывается memmove. Так или иначе вектор сдвигает элементы которые находятся старше предпологаемого индекса вставки. (всем понятно что таких операций нужно старатся избегать в векторе, сейчас вопрос не в этом)
1>>>Тогда получается что вектор нельзя инстанировать ничем иным кроме "старых добрых простых"типов.(POD) B>>Вектор может использовать специализированные алгоритмы (в том числе и с использованием memcpy/memmove) для POD-типов. Для не-POD он должен выполнять поэлементное копирование. Если у тебя vector<не-POD-тип>::insert использует memmove, то это очень странно. Было бы интересно узнать — что за компилятор, и что за реализация STL. 1>Вы абсолютно правы, я проверил под отладчиком — в случае с классом A вызываются кострукторы копирования.
1>>>Я бы хотел для себя прояснить этот момент так же с позиции почему может быть опасно двигать в памяти объект если у него нет таблицы виртуальных функций ? B>>У объекта нет таблицы виртуальных функций, у него есть указатель на нее .
B>>Вот тебе пример:
B>>
B>>Думаю, не нужно объяснять, что произойдет при вызове set для объекта, который был перемещен после создания с помощью memmove ... 1>Данный пример иллюстрирует проблему появления висячей ссылки(dead reference), это произойдёт так же в случае если на объект имеются ссылки где-то вне объекта. Эти вещи нужно учитывать, точно так же как если куда-то передаётся адрес какого-то объекта, то нужно гарантировать определённое время жизни этого объекта, что бы непроизошло обращение к уже удалённому объекту.
1>Уточню ещё раз вторую часть вопроса: в чём именно проблема с указателем на таблицу виртуальных функций ? 1>Я никак немогу понять — с самого начала хранятся два указателя — на vptable и на vftable. Сами таблицы находятся где ? Предположим мы передвинули класс, вмести с остальными данными переехали и эти два указателя, но разве их значения поменялись ? 1>Или возможно, Андрей Тарасевич имел в виду своим примером то, что сами таблицы, на которые ссылаются указатели, находятся где-то среди данных класса ? (тогда я вообще непонимаю — где ???) и после сдвига изменяются так же адреса этих таблиц ???
Ошибся, имел ввиду пример не Андрея Тарасевича а Bell-а, т.е. приведённый выше.
Здравствуйте, slegkapjan, Вы писали:
S>ВЫВОД. Объекты с виртуальными функциями и виртуальными предками в VC++ 7.1 копировать с помощью memcpy() etc. можно!
На основе практического экперимента, да еще и на совершенно не показательных примерах, такого вывода сделать, разумеется, нельзя. Разве что в юмористическом плане. Это что-то из области "безногий таракан не слышит" из диссертации ВИЧ.
class A
{
public
A(){}
~A(){}
void F();
void F1();
void F2();
private:
int m_a;
int m_b;
char m_str[100];
}
Простой как грабли.
То объекты этого класса, если я неошибаюсь, можно двигать в памяти как душе угодно(разумеется в валидных областях памяти).
memmove, memcpy and e.t.c.
Никак немогу точно вспомнить, но есть определённое ощущение, что с какого-то момента (то ли при появлении виртуального наследования, то ли при множественном наследовании и т.п.) на эту вольность накладываются ограничения.
Если можно, разъясните что можно а что нельзя, и почему нельзя.
Грабли возникают после появления вирутальных функций и вирутального наследования, т.к. переносимого способа переноса (простите за коломбур переместить эти таблицы нету. Хотя и это ограничение можно преодолеть, если создавать объект на новом месте через placement new и копировать туда исходный объект.
Здравствуйте, Анатолий Широков, Вы писали:
1>>memmove, memcpy and e.t.c.
АШ>Таким способом можно переносить объекты только POD-типов. Приведенный вами класс POD-типом не являетс
Уточню.
Т.е. вы утверждаете что класс
class A
{
public
A(){}
~A(){}
void F();
void F1();
void F2();
private:
int m_a;
int m_b;
char m_str[100];
}
копировать используя memcpy/memmove нельзя ?
Тогда возникает второй вопрос. (хотя на самом деле для меня именно он был первым).
Возможно я чего-то непонимаю, но спускаясь по дереву вызовов stl::vetor::insert()
я попадаю в конце концов в место где вызывается memmove. Так или иначе вектор сдвигает элементы которые находятся старше предпологаемого индекса вставки. (всем понятно что таких операций нужно старатся избегать в векторе, сейчас вопрос не в этом)
Тогда получается что вектор нельзя инстанировать ничем иным кроме "старых добрых простых"типов.(POD)
Я бы хотел для себя прояснить этот момент так же с позиции почему может быть опасно двигать в памяти объект если у него нет таблицы виртуальных функций ?
Здравствуйте, slegkapjan, Вы писали:
S>Грабли возникают после появления вирутальных функций и вирутального наследования, т.к. переносимого способа переноса (простите за коломбур переместить эти таблицы нету. Хотя и это ограничение можно преодолеть, если создавать объект на новом месте через placement new и копировать туда исходный объект.
Секунду.
А можно по подробнее остановится на проблеме копирования таблици виртуальных функций ?
Я об это часто слышал, но как-то внятного объяснения невстречал.
Здравствуйте, 1234, Вы писали:
1>Здравствуйте, Анатолий Широков, Вы писали:
1>>>memmove, memcpy and e.t.c.
АШ>>Таким способом можно переносить объекты только POD-типов. Приведенный вами класс POD-типом не являетс 1>Уточню. 1>Т.е. вы утверждаете что класс
... 1>копировать используя memcpy/memmove нельзя ?
Формально — нельзя, т.к., как уже было сказано, он не является POD-типом. И ограничение это накладывается стандартом языка.
Практически же копирование с использованием memcpy/memmove для подобных классов во многих случаях (читай на многих платформах) работать будет. Другой вопрос — стОит ли пренебрегать стандартом...
1>Тогда возникает второй вопрос. (хотя на самом деле для меня именно он был первым). 1>Возможно я чего-то непонимаю, но спускаясь по дереву вызовов stl::vetor::insert() 1>я попадаю в конце концов в место где вызывается memmove. Так или иначе вектор сдвигает элементы которые находятся старше предпологаемого индекса вставки. (всем понятно что таких операций нужно старатся избегать в векторе, сейчас вопрос не в этом)
1>Тогда получается что вектор нельзя инстанировать ничем иным кроме "старых добрых простых"типов.(POD)
Вектор может использовать специализированные алгоритмы (в том числе и с использованием memcpy/memmove) для POD-типов. Для не-POD он должен выполнять поэлементное копирование. Если у тебя vector<не-POD-тип>::insert использует memmove, то это очень странно. Было бы интересно узнать — что за компилятор, и что за реализация STL.
1>Я бы хотел для себя прояснить этот момент так же с позиции почему может быть опасно двигать в памяти объект если у него нет таблицы виртуальных функций ?
У объекта нет таблицы виртуальных функций, у него есть указатель на нее .
Вот тебе пример:
class bad
{
int n_;
int* p_;
public:
bad() {p_ = &n_; }
void set(int n) { *p_ = n; }
};
Думаю, не нужно объяснять, что произойдет при вызове set для объекта, который был перемещен после создания с помощью memmove ...
Здравствуйте, 1234, Вы писали:
1>Тогда возникает второй вопрос. (хотя на самом деле для меня именно он был первым). 1>Возможно я чего-то непонимаю, но спускаясь по дереву вызовов stl::vetor::insert() 1>я попадаю в конце концов в место где вызывается memmove. Так или иначе вектор сдвигает элементы которые находятся старше предпологаемого индекса вставки. (всем понятно что таких операций нужно старатся избегать в векторе, сейчас вопрос не в этом)
1>Тогда получается что вектор нельзя инстанировать ничем иным кроме "старых добрых простых"типов.(POD)
Нет, не получается.
Во-первых, внутренняя реализация библиотеки времени выполнения может делать что угодно и как угодно. У нее свои законы. Если все работает правильно, то пользователя (т.е. в данном случае — тебя) все это касаться не должно.
Во-вторых, твоя обязанность заключается в том, чтобы обеспечить свойства CopyConstructible и Assignable для объектов, которые ты хранишь в векторе. Приведенный тобой класс и так является CopyConstructible и Assignable, поэтому ничего дополнительно делать не надо.
1>Я бы хотел для себя прояснить этот момент так же с позиции почему может быть опасно двигать в памяти объект если у него нет таблицы виртуальных функций ?
Хм... Ну например объект, который содержит указатель на собственный элемент
struct N {
int i;
int* pi;
N() : pi(&i) {}
};
Если такой объект грубо передвинуть при помощи 'memcpy', то его внутренняя целостность будет нарушена. При этом никаких виртуальных функций в нем нет.
Если уж лезть в эту область, то, с практической точки зрения, наличие в объекте виртуальных функций, реализованных традиционным способом, никак не мешает его перемещаемости в памяти. Поэтому упоминание виртуальных функций тут вообще ни к селу, ни к городу.
Но это все разговоры. А с точки зрения языка двигать "сырым" копированием можно только объекты POD типов.
АТ>struct N {
АТ> int i;
АТ> int* pi;
АТ> N() : pi(&i) {}
АТ>};
АТ>
Ага, а еще объект, соржащий указатель на чужой элемент...
Я думаю, что можно вполне реализовать перемещение объектов в памяти (в т.ч. и дефрагментацию памяти) придерживаясь следующих правил:
1) везде использовать связанные умные указатели.
2) для каждого класса определить функцию, которая создавала бы копию по определенному адресу
3) не использовать указатели на члены объектов
Т.е. положим есть у нас класс:
class SomeObject {
public:
linked_ptr<SomeObject> this_ptr;
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, 1234, Вы писали:
1>>Здравствуйте, Анатолий Широков, Вы писали:
1>>>>memmove, memcpy and e.t.c.
АШ>>>Таким способом можно переносить объекты только POD-типов. Приведенный вами класс POD-типом не являетс 1>>Уточню. 1>>Т.е. вы утверждаете что класс B>... 1>>копировать используя memcpy/memmove нельзя ?
B>Формально — нельзя, т.к., как уже было сказано, он не является POD-типом. И ограничение это накладывается стандартом языка. B>Практически же копирование с использованием memcpy/memmove для подобных классов во многих случаях (читай на многих платформах) работать будет. Другой вопрос — стОит ли пренебрегать стандартом...
Я совершенго согласен — стандарт надо блюсти.
Всё дело в том что для меня стоял вопрос оптимизации по времени, в таких случаял ИМХО можно применять решения который могут немного выходить из общепринятых идиом языка, но в тоже время, они должны оставатся безопасными и универсальными.
Вот поэтому я и решил посоветоватся — "Можно ли .... ?" а не "Нужно ли.... ?"
1>>Тогда возникает второй вопрос. (хотя на самом деле для меня именно он был первым). 1>>Возможно я чего-то непонимаю, но спускаясь по дереву вызовов stl::vetor::insert() 1>>я попадаю в конце концов в место где вызывается memmove. Так или иначе вектор сдвигает элементы которые находятся старше предпологаемого индекса вставки. (всем понятно что таких операций нужно старатся избегать в векторе, сейчас вопрос не в этом)
1>>Тогда получается что вектор нельзя инстанировать ничем иным кроме "старых добрых простых"типов.(POD) B>Вектор может использовать специализированные алгоритмы (в том числе и с использованием memcpy/memmove) для POD-типов. Для не-POD он должен выполнять поэлементное копирование. Если у тебя vector<не-POD-тип>::insert использует memmove, то это очень странно. Было бы интересно узнать — что за компилятор, и что за реализация STL.
Вы абсолютно правы, я проверил под отладчиком — в случае с классом A вызываются кострукторы копирования.
1>>Я бы хотел для себя прояснить этот момент так же с позиции почему может быть опасно двигать в памяти объект если у него нет таблицы виртуальных функций ? B>У объекта нет таблицы виртуальных функций, у него есть указатель на нее .
B>Вот тебе пример:
B>
B>Думаю, не нужно объяснять, что произойдет при вызове set для объекта, который был перемещен после создания с помощью memmove ...
Данный пример иллюстрирует проблему появления висячей ссылки(dead reference), это произойдёт так же в случае если на объект имеются ссылки где-то вне объекта. Эти вещи нужно учитывать, точно так же как если куда-то передаётся адрес какого-то объекта, то нужно гарантировать определённое время жизни этого объекта, что бы непроизошло обращение к уже удалённому объекту.
Уточню ещё раз вторую часть вопроса: в чём именно проблема с указателем на таблицу виртуальных функций ?
Я никак немогу понять — с самого начала хранятся два указателя — на vptable и на vftable. Сами таблицы находятся где ? Предположим мы передвинули класс, вмести с остальными данными переехали и эти два указателя, но разве их значения поменялись ?
Или возможно, Андрей Тарасевич имел в виду своим примером то, что сами таблицы, на которые ссылаются указатели, находятся где-то среди данных класса ? (тогда я вообще непонимаю — где ???) и после сдвига изменяются так же адреса этих таблиц ???
Здравствуйте, slegkapjan, Вы писали:
1>>А можно по подробнее остановится на проблеме копирования таблици виртуальных функций ? 1>>Я об это часто слышал, но как-то внятного объяснения невстречал.
S>Коротко говоря, для хранения класса в памяти компилятор использует приблизительно такую структуру: S>struct X { S>void *vptable; S>void *vftable; S>данные S>... S>};
S>vptable указывает на таблицу вирутальных предков нашего класса, а vftable указывает на таблицу виртуальных функций. Соответственно отсюда вытекает все то, о чем я говорил. Внутреняя кухня довольно простая, в частности советую попробовать такой код:
S>class X { S>public: S>void f() { printf("%p",this); } S>};
S>... S>X *x=0; x->>f();
S>Результатом выполнения этой программы будет вывод на экран 0
S>Соответственно, если сделать так: S>class X { S>public: S>virtual void f() { printf("%p",this); } S>};
S>то при попытке выполнения произойдет ошибка, т.к. программа не сможет найти указатель на таблицу вирутальных функций...
Этот пример мне вполне понятен.
А вот здесь
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Здравствуйте, slegkapjan, Вы писали:
АТ>>>
АТ>>>struct N {
АТ>>> int i;
АТ>>> int* pi;
АТ>>> N() : pi(&i) {}
АТ>>>};
АТ>>>
S>>Ага, а еще объект, соржащий указатель на чужой элемент...
АТ>Не совсем понимаю, что имеется в виду.
Возможно slegkapjan имеет ввиду случай с вектором — при вставке объекты передвинутся на смещение кратное размеру каждого элемента — и тогда точно по этому адресу который хранил pi будет лежать int i другого объекта.
Я только так это понимаю.
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, 1234, Вы писали:
1>>Если можно, разъясните что можно а что нельзя, и почему нельзя.
CS>Можно "двигать" все. С++ тебе этого не запрещает. (И хорошо!) CS>Два "но" CS>1) ты отдаешь себе отчет в том что происходит. CS>2) "двигаемые" объекты позиционно независимы (что как правило хорошая практика).
Согласен, речь идёт о случае "знаю что делаю и для чего", но можно ли ?( и когда нельзя ?) CS>Объекты содержащие vtbl тоже прекрасно двигаются если dst корректно выровнено.
CS>К сожалению в базовых коллекциях STL нет семантики move или transfer ownership. CS>Но по моему сия проблема обсуждалась как раз в working group в последнее время.
Ну вот, теперь мнения разошлись....
Так вы братцы меня совсем запутаете ?
Здравствуйте, 1234, Вы писали:
1>Здравствуйте, c-smile, Вы писали:
CS>>Здравствуйте, 1234, Вы писали:
1>>>Если можно, разъясните что можно а что нельзя, и почему нельзя.
CS>>Можно "двигать" все. С++ тебе этого не запрещает. (И хорошо!) CS>>Два "но" CS>>1) ты отдаешь себе отчет в том что происходит. CS>>2) "двигаемые" объекты позиционно независимы (что как правило хорошая практика). 1>Согласен, речь идёт о случае "знаю что делаю и для чего", но можно ли ?( и когда нельзя ?)
Когда можно:
1) ты отдаешь себе отчет в том что происходит.
2) "двигаемые" объекты позиционно независимы (что как правило хорошая практика).
следствие из пункта 1) и 2) : и ты не используешь STL или какой другой toolkit где выполняется п.2. для чего тебе нужно пройти п. 1
1>Ну вот, теперь мнения разошлись....
Где разошлись? Не вижу...
"Голимый" С++ рарешает все что не запрещено.
STL же накладывает свою систему ограничений. И это нормально. by design что называтеся. Об этом
тебе и пытаются сказать забывая указать что stl это не сам C++, а бибиотека для него.
1>Так вы братцы меня совсем запутаете ?
1>>Ну вот, теперь мнения разошлись....
CS>Где разошлись? Не вижу...
CS>"Голимый" С++ рарешает все что не запрещено. CS>STL же накладывает свою систему ограничений. И это нормально. by design что называтеся. Об этом CS>тебе и пытаются сказать забывая указать что stl это не сам C++, а бибиотека для него.
причем здесь STL, перемещение (memmove) и копирование (memcpy) разрешено только для POD-типов и это регулирует стандарт — так что тень на плетень не надо наводить и так ночь на дворе.
Здравствуйте, Анатолий Широков, Вы писали:
АШ>причем здесь STL, перемещение (memmove) и копирование (memcpy) разрешено только для POD-типов и это регулирует стандарт — так что тень на плетень не надо наводить и так ночь на дворе.
c-smile,
> АШ> причем здесь STL, перемещение (memmove) и копирование (memcpy) разрешено только для POD-типов и это регулирует стандарт — так что тень на плетень не надо наводить и так ночь на дворе.
> Где это такое написано? Ссылку даем, да?
Стандарт гарантирует (3.9) возможность побайтного копирования только для POD-типов. В остальных случаях гарантий, что в результате побайтного копирования будет получен эквивалентный или даже валидный объект, нет.
Например, можно представить себе (экзотичную, но допустимую с точки зрения стандарта) реализацию виртуальных функций в виде соответствия <адрес объекта> -> <VMT>, которая при перемещении объектов полиморфных классов побайтным копированием работать не будет. Также можно пофантазировать и на тему объектов, хранящихся в памяти не непрерывно...
Posted via RSDN NNTP Server 1.9 delta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Стандарт гарантирует (3.9) возможность побайтного копирования только для POD-типов. В остальных случаях гарантий, что в результате побайтного копирования будет получен эквивалентный или даже валидный объект, нет.
ПК>Например, можно представить себе (экзотичную, но допустимую с точки зрения стандарта) реализацию виртуальных функций в виде соответствия <адрес объекта> -> <VMT>, которая при перемещении объектов полиморфных классов побайтным копированием работать не будет. Также можно пофантазировать и на тему объектов, хранящихся в памяти не непрерывно...
Т.е. не написано что "memmove" к не POD типам применять нельзя. Правильно?
"Также можно пофантазировать и на тему объектов, хранящихся в памяти не непрерывно..."
Давай рассуждать здраво....
Наличие оператора new placement гарантирует что объект выделится а) непрерывным куском и б) займет sizeof(T).
"<адрес объекта> -> <VMT>" теоретически возможно, но практически не реально совсем.
Если так будет то все библиотеки C++ object persistence / memory mapping улетают в трубу.
Здравствуйте, Андрей Тарасевич, Вы писали:
ПК>>>Стандарт гарантирует (3.9) возможность побайтного копирования только для POD-типов. В остальных случаях гарантий, что в результате побайтного копирования будет получен эквивалентный или даже валидный объект, нет.
CS>>Т.е. не написано что "memmove" к не POD типам применять нельзя. Правильно?
АТ>Нет, не написано. Тем не менее вышепроцитированное именно означает, что "memmove к не POD типам применять нельзя". Точнее, применять то ты его можешь сколько угодно, но вот только ожидать, что таки способом можно скопировать не-POD объект из одного места памяти в другое не приходится.
Что значит "нельзя"? Не гарантируется стандартом, да. Но не нельзя. Есть же разница?
Господа, я не призываю двигать все и вся.
Я утверждаю следущие — если ты до конца отдаешь себе отчет что происходит в данном конкретном случае то можно.
С++ это язык для ответсвенных людей. Это не для чайников как некоторые другие языки.
Можно например сделать в C руками vtbl и объект и скормить это безобразие в C++.
И куча людей тебе скажет спасибо! И книжек море напишет.
Я говорю про принципы на которых построены и держатся COM и CORBA.
Скажите тогда свое "нельзя" авторам и пользователям данных технологий.
CS>>Наличие оператора new placement гарантирует что объект выделится а) непрерывным куском АТ>Непрерывнум куском "сырой" памяти — да, гарантирует. CS>>б) займет sizeof(T). АТ>Нет, разумеется. Ничего такого не гарантируется. Гарантируется, что объект займет не менее чем 'sizeof(T)'. За примерами далеко ходить не надо — динамически выделяемые массивы занимают больше, чем 'sizeof' соответствующего массивного типа.
Пардон, но именно гарантируется sizeof(T)/
Только если ты специально опишешь свою версию функции new то тебе удастся под объект аллоцировать больше.
По умолчанию же у тебя есть две опции применения оператора new (и две стандартные имплементации функций new):
new typename
new(location) typename.
Как минимум вторая стандартная форма гарантируют что объект "начнется" по адресу location и не выйдет за
((typename*)location + 1).
Здравствуйте, Bell, Вы писали:
1>>Тогда получается что вектор нельзя инстанировать ничем иным кроме "старых добрых простых"типов.(POD) B>Вектор может использовать специализированные алгоритмы (в том числе и с использованием memcpy/memmove) для POD-типов. Для не-POD он должен выполнять поэлементное копирование. Если у тебя vector<не-POD-тип>::insert использует memmove, то это очень странно. Было бы интересно узнать — что за компилятор, и что за реализация STL.
Есть предположение, что это идет вызов конструктора копирования. У него ведь он в классе не определен, т. е. используется конструктор копирования по-умолчанию.
c-smile,
> АТ> Нет, не написано. Тем не менее вышепроцитированное именно означает, что "memmove к не POD типам применять нельзя". Точнее, применять то ты его можешь сколько угодно, но вот только ожидать, что таки способом можно скопировать не-POD объект из одного места памяти в другое не приходится.
> Что значит "нельзя"? Не гарантируется стандартом, да. Но не нельзя. Есть же разница?
Можно еще точнее: приводит к неопределенному поведению. В терминах стандарта это одно и то же, что и "нельзя".
Переходя же в область практических рассуждений, согласен: иногда можно и нужно. Понимая, что хак, что с другим компилятором или со следующей версией этого может перестать работать и т.п. В общем, как ты верно говоришь: "если ты до конца отдаешь себе отчет что происходит в данном конкретном случае".
> CS>>б) займет sizeof(T).
> АТ> Нет, разумеется. Ничего такого не гарантируется. Гарантируется, что объект займет не менее чем 'sizeof(T)'. За примерами далеко ходить не надо — динамически выделяемые массивы занимают больше, чем 'sizeof' соответствующего массивного типа.
> Пардон, но именно гарантируется sizeof(T)/
> Только если ты специально опишешь свою версию функции new то тебе удастся под объект аллоцировать больше.
Уточняем:
5.3.4/10 A new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array.
> По умолчанию же у тебя есть две опции применения оператора new (и две стандартные имплементации функций new): > > new typename > new(location) typename. > > Как минимум вторая стандартная форма гарантируют что объект "начнется" по адресу location и не выйдет за > ((typename*)location + 1).
В случае массивов все сложнее (читай "хуже"):
[Example:
— new T results in a call of operator new(sizeof(T)),
— new(2,f) T results in a call of operator new(sizeof(T),2,f),
— new T[5] results in a call of operator new[](sizeof(T)*5+x), and
— new(2,f) T[5] results in a call of operator new[](sizeof(T)*5+y,2,f).
Here, x and y are non-negative unspecified values representing array allocation overhead; the result of the new-expression will be offset by this amount from the value returned by operator new[]. This overhead may be applied in all array new-expressions, including those referencing the library function operator new[](std::size_t, void*) and other placement allocation functions. The amount of overhead may vary from one invocation of new to another.]
Возвращаясь к фантазиям на тему не непрерывных объектов, можно заметить, что реализация может легко различать объекты, созданные через placement new, и через "обычный" new. Мотивы разработчиков такого чуда оставляем за кадром
Posted via RSDN NNTP Server 1.9 delta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
#pragma pack(push,1)
class X {
public:
int i;
void f();
};
#pragma pack(pop)
то sizeof(X)==4, т.е. выделяется память только для int i;
Если же написать
#pragma pack(push,1)
class X {
public:
int i;
virtual void f();
};
#pragma pack(pop)
то sizeof(X)==8! т.е. также выделяется память и для указателя на vftable
Такой код дает тоже 8 байт:
class Z {
};
#pragma pack(push,1)
class X: virtual public Z {
public:
int i;
void f();
};
#pragma pack(pop)
А такой -- 12:
class X: virtual public Z {
public:
int i;
virtual void f();
};
Таким образом мы делаем вывод, что VC++ 7.1 использует описанную мной выше структуру. Теперь можно пробовать копировать класс с вирутальной функцией и виртуальным предком.
class Z {
};
#pragma pack(push,1)
class X: public virtual Z {
public:
int a;
virtual void f();
};
#pragma pack(pop)
void X::f() {
printf("X::f()");
}
void main() {
X x1;
int sz=sizeof(x1);
char ch1[sizeof(X)];
memcpy(ch1,&x1,sizeof(X));
X *x2=(X *)ch1;
x2->f();
}
все прекрасно работает! т.е. на экран выводится ожидаемое X::f().
N.B. нормальные деструкторы я делать не стал, важно было проверить правильность идеи.
ВЫВОД. Объекты с виртуальными функциями и виртуальными предками в VC++ 7.1 копировать с помощью memcpy() etc. можно!
АТ>На основе практического экперимента, да еще и на совершенно не показательных примерах, такого вывода сделать, разумеется, нельзя.
Т.е. вы считаете, что "практический эксперимент" не может приводить к каким-то выводам?
И какие эксперименты вы считаете "показательными"?
CS>И лирика: на самом деле такие вот возможности (не обязательно эти) и делают C++ тем чем он есть — платформой для создания эффективных програм. С++ ффективный и универсальный но не "дуракоустойчивый". Что есть то есть.
5 баллов.
Здравствуйте, 1234, Вы писали:
1>То объекты этого класса, если я неошибаюсь, можно двигать в памяти как душе угодно(разумеется в валидных областях памяти). memmove, memcpy and e.t.c.
B>>Думаю, не нужно объяснять, что произойдет при вызове set для объекта, который был перемещен после создания с помощью memmove ... 1>Данный пример иллюстрирует проблему появления висячей ссылки(dead reference), это произойдёт так же в случае если на объект имеются ссылки где-то вне объекта. Эти вещи нужно учитывать, точно так же как если куда-то передаётся адрес какого-то объекта, то нужно гарантировать определённое время жизни этого объекта, что бы непроизошло обращение к уже удалённому объекту.
CS>Экзепмпляры виртуальных классов "двигать" в принципе можно, но отдавая себе отчет что такое решение потенциально не portable т.е.зависит от конкретного компилятора. Хотя все известные мне компиляторы и safe в этом
Борландовский компилятор 5.х (возможно, это был какой-то g++ но по-моему, все ж таки борланд 5.5) для определения положения подобъекта виртуального базового класса использует НЕПОСРЕДСТВЕННЫЙ адрес. Копируй наздоровье свои объекты как хочешь, но не говори, что это делать можно. Можно писать и int i = 0; i = i++ + ++i; если тебе так охота. Только опять же, не надо говорить, что так можно, "потому что ты знаешь, что делаешь".
Of course, the code must be complete enough to compile and link.
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, Павел Кузнецов, Вы писали:
ПК>>Уточняем: ПК>>
5.3.4/10 A new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array.
CS>Ну а я о чем говорил? (bold emphasizing is mine)
CS>
CS>void *someptr;
CS>выражение:
CS>new(someptr) T;
CS>вызовет placement функцию ::operator new с параметрами
CS>::operator new( sizeof(T), someptr )
CS>
CS>Возвращаясь же к практическим соображениям и резюмируя вышесказанное (с глубоким почтением ко всем участникам) еще раз рискну заметить:
CS>Не виртуальные классы (т.е. "простые" но уже не POD) "двигать" достаточно безопасно. CS>Экзепмпляры виртуальных классов "двигать" в принципе можно, но отдавая себе отчет что такое решение потенциально не portable т.е.зависит от конкретного компилятора. Хотя все известные мне компиляторы и safe в этом плане, проверять все равно надо. Условно говоря такое решение — на совести разработчика и мотвационных установок проекта.
CS>И лирика: на самом деле такие вот возможности (не обязательно эти) и делают C++ тем чем он есть — платформой для создания эффективных програм. С++ ффективный и универсальный но не "дуракоустойчивый". Что есть то есть. что верно — то верно!
Благодарю всех за дискуссию, внимательно следил за развитием топика.
К сожалению, раc стандарт гласит что можно "двигать" только POD типы, то совершенно спокойным быть нельзя даже если класс проще молотка, т.к. существует вероятность что на каком-нибудь компиляторе, где-то когда-то этот метод может перестать работать.
Однако, так же, не теряя здравого смысла, и учитывая ВСЁ сказанное выше(и втом числе последнее резюмирующее собщение от c-smile), в целях оптимизации(и только ради неё!) принял решение, что это допустимо для класса у которого нет ни виртуального наследования, ни виртуальных функций-членов(и даже деструктор невиртуальный хоть это и неочень хорошо).
На том и порешил.
Всем спасибо.
Если у кого будет что добавить по теме — welcome!
Здравствуйте, c-smile, Вы писали:
CS>Здравствуйте, Lorenzo_LAMAS, Вы писали:
CS>Что такое "НЕПОСРЕДСТВЕННЫЙ адрес" и чем оно отличается от просто "адрес"?
Я хотел подчеркнуть, что хранится не указатель на какую-то часть таблицы, с помощью котрого и которой найдется подобъект, а адрес этого подобъекта.
CS>Например для VC++ вот это полезно знать:
А вот за ссылку спасибо.
Of course, the code must be complete enough to compile and link.
Здравствуйте, 1234, Вы писали:
1>Уточню ещё раз вторую часть вопроса: в чём именно проблема с указателем на таблицу виртуальных функций ?
С указателем на таблицу виртуальных функций проблем нет никаких. Могут быть проблемы при наличии виртуального наследования.
1>Я никак немогу понять — с самого начала хранятся два указателя — на vptable и на vftable.
Если нет виртуального наследования, то указатель только один -- на глобально определённую (одну на все экземпляры класса) таблицу виртуальных функций.
1>Сами таблицы находятся где ? Предположим мы передвинули класс, вмести с остальными данными переехали и эти два указателя, но разве их значения поменялись ?
Соответственно, при наличии отсутствия виртуального наследования, ответ НЕТ.
1>Или возможно, Андрей Тарасевич имел в виду своим примером то, что сами таблицы, на которые ссылаются указатели, находятся где-то среди данных класса ?
Нет.
1>(тогда я вообще непонимаю — где ???) и после сдвига изменяются так же адреса этих таблиц ???