3

好吧,我最近遇到了一个让我分心的情况,如下所示:

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_objBase 类转换为 a void*,并将其转换SetPyObj()为 this :

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

Base::SetObj(obj)看起来像这样:

void SetObj(void *obj);

(我选择了void *,因为出于某些充分的原因, Base 定义中没有 PyObject 类型......)

这样我写到父类,问题就消失了,但仍然......

我对这种操作不放心,对解决方法也不是很满意(这看起来不是一个好的做法,我能闻到它的味道!)。

所以,这是我的问题:

只要我Base通过仅使用其他方法(没有其他成员变量)的类扩展我所做的事情是否“可以接受”?

如果不是,那么在这种情况下是否可以接受沮丧?在哪些情况下可以接受向下转换(我的意思是除了首先创建some_ObjectPtr为 a之外Derived*)?

如果是,那么处理这种情况的优雅方法是什么?

注意:dynamic_cast<>在我的环境中使用类似的功能不是一个选项(Cython 不可用),我不确定它是否会有所帮助。

额外的第二个问题:您能解释一下如何处理给定类实例的内存大小吗?我可以清楚地看到,在我的示例中,附加属性按其大小(64 位)溢出,但添加一个方法似乎是安全的(这是否意味着如果派生类仅包含附加方法,则派生类和基类都会生成完全相同的尺寸?)。

我希望我把我的担忧说清楚了。

4

1 回答 1

1

只有当这个对象Derived*被创建为Derived或任何继承自Derived. 在其他情况下,它是不安全的。它可能适用于某些实现,但语言不允许。

(我选择了 void *,因为出于某些很好的原因,Base 定义中没有 PyObject 类型......)

这不是一个很好的理由。你永远不应该void*在 C++ 中使用。您应该在标题顶部放置 PyObject 的前向声明。考虑到 PyObject 的真实声明,前向声明语句应如下所示:

typedef struct _object PyObject;
于 2013-06-09T07:49:03.550 回答