如果派生类只包含方法(没有成员变量),那么向下转换是否安全

时间:2013-06-09 03:09:05

标签: c++ downcast

好吧,我最近遇到的情况是,我分散注意力,如下所示:

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类都将生成对象完全相同的尺寸?)。

我希望我明白我的担忧。

1 个答案:

答案 0 :(得分:1)

仅当此对象已创建为Derived*或从Derived继承的任何类时,才能将对象指针向下转发Derived。在其他情况下,它可能不安全。它可能适用于某些实现,但语言不允许这样做。

  

(我选择了void *,因为出于某些原因,PyObject类型   无法从基本定义中获得...)

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

typedef struct _object PyObject;