好吧,我最近遇到的情况是,我分散注意力,如下所示:
class Derived: public Base {
public:
PyObject *GetPyObj() { return m_obj; }
void SetPyObj(PyObject *obj) { m_obj = obj }
private:
PyObject *m_obj;
};
这个类在Base类中添加了一个额外的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
类(没有其他成员变量),做我所做的事情是否“可接受”?
如果没有,那么在这样的背景下贬低是否可以接受?在什么情况下可以接受向下转换(我的意思是除了首先创建some_ObjectPtr
作为Derived*
之外)?
如果是的话,处理这种情况的优雅方法是什么?
注意:使用像dynamic_cast<>
这样的功能在我的环境中不是一个选项(不适用于Cython),我不确定它是否会有所帮助。
奖金第二个问题:你能解释一下如何处理某个类的给定实例的内存大小吗?我可以清楚地看到,在我的示例中,附加属性以其大小(64位)溢出,但添加方法似乎是安全的(这是否意味着如果派生类仅包含其他方法,则Derived和Base类都将生成对象完全相同的尺寸?)。
我希望我明白我的担忧。
答案 0 :(得分:1)
仅当此对象已创建为Derived*
或从Derived
继承的任何类时,才能将对象指针向下转发Derived
。在其他情况下,它可能不安全。它可能适用于某些实现,但语言不允许这样做。
(我选择了void *,因为出于某些原因,PyObject类型 无法从基本定义中获得...)
这不是一个好理由。你永远不应该在C ++中使用void*
。您应该在标题的顶部放置PyObject的前向声明。考虑到PyObject的真实声明,前向声明语句应如下所示:
typedef struct _object PyObject;