Безопасно ли понижать рейтинг, если производный класс содержит только методы (без переменных-членов)

Ну, я недавно столкнулся с делом, где меня сбили, отвлекаясь следующим образом:

class Derived: public Base {
public:
PyObject *GetPyObj() { return m_obj; }
void SetPyObj(PyObject *obj) { m_obj = obj }
private:
PyObject *m_obj;
};

Этот класс добавляет дополнительный m_obj К сожалению, я приписал атрибут базовому классу где-то в моем коде совершенно небезопасным приведением типа:

Derived *node = <Derived *>some_ObjectPtr;
// Where "some_ObjectPtr" had been created as a "Base*" in the first place.

Что иногда приводило к произвольному поведению, когда я звонил SetPyObj(), Благодаря ValgrindЯ смог определить, что я «писал за пределами моего объекта», и найти проблему:

...
==5679==
==5679== Invalid write of size 8
==5679==    at 0x812DB01: elps::Derived::SetPyObj(_object*) (ALabNetBinding.cpp:27)
==5679==    by 0x8113B2C: __pyx_f_5cyelp_12PyLabNetwork_Populate(__pyx_obj_5cyelp_PyLabNetwork*, int, int) (cyelp.cpp:10112)
...

Моя первая попытка была сдвинуть m_obj в базовый класс как void*и преобразовать SetPyObj() в это:

void SetPyObj(PyObject *obj) {
this->SetObj(obj);
}

где Base::SetObj(obj) выглядит так:

void SetObj(void *obj);

(Я выбрал void *, потому что по ряду веских причин тип PyObject не был доступен в определении Base …)

Таким образом я писал в родительский класс, и проблема исчезла, но все же …

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

Итак, вот мой вопрос:

Допустимо ли делать то, что я делал, пока я Base классом только с дополнительными методами (без дополнительных переменных-членов)?

Если нет, то приемлемо ли в любом случае опускаться в таком контексте? В каких случаях приемлемо снижение рейтинга (я имею в виду, не создавая some_ObjectPtr как Derived* на первом месте)?

Если да, то какой был бы элегантный способ справиться с этим делом?

Примечание: использование таких функций, как dynamic_cast<> не было выбора в моей среде (недоступно в Cython), и я не уверен, что это все равно помогло бы.

Дополнительный дополнительный вопрос: не могли бы вы объяснить, как обрабатывается размер в памяти данного экземпляра класса? Я ясно вижу, что в моем примере дополнительный атрибут переполняется его размером (64 бита), но добавление метода кажется безопасным (означает ли это, что если производный класс содержит только дополнительные методы, то и классы Derived и Base будут создавать объекты точно такой же размер?).

Я надеюсь, что я ясно выразил свои опасения.

1

Решение

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

(Я выбрал void *, потому что по ряду веских причин тип PyObject
не было доступно из базового определения …)

Это не веская причина. Вы никогда не должны использовать void* в C ++. Вы должны поместить предварительное объявление PyObject в верхней части вашего заголовка. Учитывая реальное объявление PyObject, оператор предварительного объявления должен выглядеть следующим образом:

typedef struct _object PyObject;
1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]