Преобразование структуры POD в производный тип

Работая над устаревшим проектером, я столкнулся со следующей закономерностью: структура POD используется для передачи данных по сети.

struct PODType {
    // some data
    int data;
};

На стороне получателя данные принимаются в объект типа POD. Позже класс является производным от PODType, и полученный объект приводится с помощью приведения в стиле C в производный класс, чтобы использовать некоторые методы для доступа к данным.

class DerivedFromPOD: public PODType {
public:
    // some methods
    int f(int x) {return data+x;}
protected:
    // some methods
};

PODType pod;
receive(&pod);

DerivedFromPOD* d = (DerivedFromPOD*)&pod;
int i = d->f(10);

Производный класс имеет общедоступные и защищенные методы, поэтому он больше не является POD. Я знаю, что это злоупотребление наследованием, но оно уже давно присутствует в кодовой базе.

Мне интересно, гарантированно ли это работает со стандартной точки зрения (С++ 03 или С++ 98). Производный класс не имеет собственных элементов данных или виртуальных функций, но я не уверен, гарантирует ли он идентичность макета памяти, учитывая, что один POD, а другой нет. Компилятор вынужден организовать DerivedFromPOD таким образом, чтобы адрес d.data был идентичен адресу объекта d типа DerivedFromPOD, как для базового класса POD?


person Jens    schedule 13.05.2014    source источник
comment
и это даже не стандартный макет в терминах C++11 А? Почему нет? Насколько я вижу, и PODType, и DerivedFromPOD являются POD в C++11.   -  person dyp    schedule 13.05.2014
comment
То, как структура данных размещается в памяти, зависит от поведения компилятора и ABI, стандарт не гарантирует этого. Я думаю, что в этом случае указатель на неполный тип может быть более выгодным. Также помните, что приведение в стиле C в C++ в целом очень плохо.   -  person user2485710    schedule 13.05.2014
comment
Это вызывает неопределенное поведение в соответствии со строгими правилами псевдонимов.   -  person M.M    schedule 13.05.2014
comment
@dyp Неверно прочитанный раздел 9.7 имеет одинаковый контроль доступа [...] для всех нестатических элементов данных. Пост отредактирую.   -  person Jens    schedule 13.05.2014
comment
@dyp Но когда я снова посмотрел на исходный код, я понял, что пропустил определение члена (класс на самом деле довольно большой), который, безусловно, не является типом POD, и поэтому мой PODType также не является POD.   -  person Jens    schedule 13.05.2014
comment
Можете ли вы изменить DerivedFromPOD, чтобы иметь PODType в качестве члена, а затем построить из PODType? Это было бы намного безопаснее и не должно быть так сложно.   -  person Rob K    schedule 13.05.2014
comment
Это то, что я уже предлагал, но я не уверен, что смогу сделать это прямо сейчас. Исходный код принадлежит не нам, а заказчику, и мы не должны изменять код, не имея связанного с ним сообщения об ошибке. Я знаю, что это не оптимально и в коде еще много проблем, но заказчик хочет именно так.   -  person Jens    schedule 13.05.2014


Ответы (2)


Конечно, это не гарантирует работу в целом (попробуйте добавить виртуальную функцию в производный класс) и формально является неопределенным поведением. (Я также не понимаю, как можно использовать struct для передачи данных по сети. Разные машины будут представлять его по-разному.)

person James Kanze    schedule 13.05.2014

Указатель DerivedFromPOD* можно безопасно преобразовать в указатель PODType*. Таким образом, мы уверены, что расположение унаследованного PODType в памяти такое же.

Однако при приведении в обратном направлении DerivedFromPOD может быть составлено в памяти с некоторыми данными компилятора, затем с данными PODType, затем с некоторыми дополнительными данными компилятора.

Если вы используете приведение в стиле C или static_cast<> для этого приведения, компилятор предположит, что вы знаете, что делаете, и отрегулирует адрес указателя так, чтобы PODType часть DerivedFromPOD правильно указывала на правильную область.

Однако не пытайтесь использовать методы, которые будут обращаться к другим данным из DerivedFromPOD, так как они не будут правильно храниться в памяти.

В частности, не используйте никакие виртуальные методы, так как отсутствует VMT (таблица виртуальных методов).

person Wizou    schedule 13.05.2014
comment
он кастует в другую сторону, от PODType к Derived. Кроме того, класс не является полиморфным, поэтому у него нет vtable. - person M.M; 13.05.2014
comment
Внимательно прочитайте мой ответ. Я начал с того, как это безопасно, также как ответ, связанный с его вопросом о расположении в памяти. Тогда отвечаю про PODType -> Derived cast. Моя строка о виртуальном методе здесь не применима, но я хотел, чтобы это был полный ответ. - person Wizou; 13.05.2014
comment
Я не вижу, где вы отвечаете на приведение PODType-›Derived. Пункты 1-3 говорят о Derived-›Pod, а пункт 4 неясен. - person M.M; 13.05.2014