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 типов.
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. извиняюсь за небольшое лирическое отступление
АТ>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>то при попытке выполнения произойдет ошибка, т.к. программа не сможет найти указатель на таблицу вирутальных функций...
Этот пример мне вполне понятен.
А вот здесь
Здравствуйте, 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-а, т.е. приведённый выше.
Здравствуйте, 1234, Вы писали:
1>Если можно, разъясните что можно а что нельзя, и почему нельзя.
Можно "двигать" все. С++ тебе этого не запрещает. (И хорошо!)
Два "но"
1) ты отдаешь себе отчет в том что происходит.
2) "двигаемые" объекты позиционно независимы (что как правило хорошая практика).
Объекты содержащие vtbl тоже прекрасно двигаются если dst корректно выровнено.
К сожалению в базовых коллекциях STL нет семантики move или transfer ownership.
Но по моему сия проблема обсуждалась как раз в working group в последнее время.
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Здравствуйте, 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
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен