2015-03-20 1 views
0

Согласно различным источникам, которые я читал, следующий C++ код вызывает неопределенное поведение:Почему нельзя копировать объекты не-POD с помощью memcpy?

class A { 
public: 
    virtual void method() { 
     std::cout << "Hello" << std::endl; 
    } 
}; 

... 
A *a, *b; 
// obtain 2 Instances from somewhere, then... 
memcpy (a, b, sizeof(A)); 
a->method(); 

Почему неопределенные эта причина поведения? Я не вижу логической реализации языка, где это не будет вести себя так, как ожидалось (в случае, когда оба объекта имеют одинаковый тип времени выполнения), поэтому почему разработчики языка решили сделать это неопределенным?

ответ

3

memcpy делает мелкую копию. В общем случае выполнение мелкой копии небезопасно для типов, которые не являются тривиально-скопируемыми, например, потому что тип имеет конструктор копирования, который должен сделать что-то особенное.

Конечно, вы можете придумать пример, где на самом деле мелкая копия с использованием memcpy «работает», но тогда стандарт должен был бы перечислять все случаи, которые разрешены, и это потребует знания всех допустимых вариантов реализации и оценки что будет работать.

Вместо этого стандарт просто говорит, что вы не можете этого сделать, что является правильным выбором ИМХО.

Используйте memcpy для копирования необработанных байтов. Используйте конструктор копирования для копирования объектов. Это простое, логичное правило.

+0

Это не может быть * безопасным * в общем случае для этого, но последствия этого должны быть абсолютно познаваемыми, если вы понимаете реализацию рассматриваемого класса. Я по-прежнему не вижу причин, почему неопределенное поведение здесь полезно для разработчиков языков или разработчиков. – Jules

+0

Это не полезно для разработчиков, это UB, чтобы остановить идиотов, стреляющих в ногу. Использование 'memcpy' для копирования инкапсуляции класса. Если автор класса определил конструктор копии или другую нетривиальную специальную функцию-член, то, предположительно, у них есть все основания для этого, а копирование другими средствами может привести к нарушению инвариантов класса.Если класс виртуальный, есть вещи, которые происходят за кулисами, которые компилятор делает для инициализации класса, пользователи не должны создавать объекты, минуя эту инициализацию и создавая объекты frankenstein, которые не были построены. –

+0

Или, говоря иначе, язык говорит: «что вы хотите сделать, разбивает объектную модель C++, которая не поддерживается». –

3

Почему это вызывает неопределенное поведение?

Поскольку язык не указывает, как реализуется полиморфизм, и поэтому не может указывать, что полиморфные типы будут тривиально скопируемыми.

Я не вижу логической реализации языка, где это не будет вести себя, как и ожидалось

полиморфизм может быть реализован в виде карты от объекта адресов к типу информации, а не указатель, хранимый в каждом объекте , Можно было бы обсудить, будет ли это лучше или хуже, но это, конечно, нелогично.

Почему дизайнеры языка решили сделать это неопределенным?

Так, чтобы не ограничивать возможность появления лучших реализаций полиморфизма. Ваша неспособность увидеть альтернативы не обязательно означает, что они не существуют.

+0

AFAICT существует только 2 категории возможных реализаций полиморфизма: те, в которых тип объекта является неотъемлемым для данных объекта, как в vtable, или тех, где используется какой-либо внешний метод ассоциирования типа, а данные объекта интерпретируются только как объявленные элементы данных. Но в любом случае этот сценарий (где оба источника и получателя имеют один и тот же тип) будет гарантированно работать: если он является внутренним, тип объекта будет скопирован и, следовательно, оставлен без изменений, а если внешний механизм будет нетронутым, а информация типа не изменится. – Jules

+0

@jules: Это может быть или не быть, но стандарт не пытался перечислить все возможные реализации или даже те, которые вы можете себе представить. Он оставил реализацию до разработчика, не ограничивая их, чтобы класс оставался тривиальным. Полиморфные классы обычно являются нетривиальными и другими способами, и вы редко хотите сочетать байт-споры с абстракциями высокого уровня, что кажется мне вполне разумным. –

2

Если вы используете memcpy, вы обходите конструкторы копирования объекта. Это оставляет вашу программу в некогерентном состоянии (иначе это неопределенное поведение).

+0

Да, но это должно быть предсказуемым состоянием (например, если вы копируете объект, содержащий ресурс, этот ресурс будет дважды освобожден, если вы не сделаете какой-то шаг для его предотвращения). – Jules