5

您能否详细说明为什么此代码在提到的地方崩溃?我对此有点困惑。我想这与它有关,sizeof(int)但我不太确定。谁能解释一下?

class Base
{
public:
    virtual void SomeFunction() 
    {
        printf("test base\n");
    }

    int m_j;
};

class Derived : public Base {
public:
   void SomeFunction() 
   {
       printf("test derive\n");
   }

private:
   int m_i;
};

void MyWonderfulCode(Base baseArray[])
{
   baseArray[0].SomeFunction();  //Works fine
   baseArray[1].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[2].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[3].SomeFunction();  //Works fine
   baseArray[4].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[5].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[6].SomeFunction();  //Works fine
   baseArray[7].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[8].SomeFunction();  //Crashes because of invalid vfptr
   baseArray[9].SomeFunction();  //Works fine
}
int _tmain(int argc, TCHAR* argv[])
{
   Derived derivedArray[10];
   MyWonderfulCode(derivedArray);
   return 0;
}
4

7 回答 7

16

sizeof(Derived)大于sizeof(Base)。这就是原因。

Foo通常,在 index 处索引对象数组的i工作方式如下:

element_address = array_base_address + i * sizeof(Foo)

如果数组元素不是预期的大小,您可以看到此索引如何中断。为什么它适用于某些索引是因为有时计算的元素地址指向内存中的有效对象(但它实际上不是i第一个对象)。

于 2009-06-25T11:32:49.553 回答
13

永远不要多态地处理数组。C++ 语言不支持这一点。(另请参阅此相关问题。)

于 2009-06-25T11:29:26.503 回答
12

引用此常见问题解答:派生数组与基数组相同吗?

Derived 大于 Base,用第二个对象 baseArray 做的指针运算不正确:编译器在计算第二个对象的地址时使用 sizeof(Base),但数组是 Derived 的数组,这意味着计算的地址(以及后续成员函数 f()) 的调用甚至不在任何对象的开头!它位于 Derived 对象的中间。

于 2009-06-25T11:31:22.630 回答
9

数组不能用于存储不同类型的对象或用于多态处理对象 - 而是存储指针。

Base和的大小Derived不同 - 看到Base[]数组的代码无法检测到它真的是一个Derived[]数组,只是错误地索引了数组,从而产生了各种未定义的行为。

于 2009-06-25T11:30:58.170 回答
4

这个问题称为剪切。它在其中有效地剥离了对象的继承部分,这导致 V 表指向缺少的函数。

解决方案是使用指针数组。试试这个:

void MyWonderfulCode(Base* baseArray)
{
   baseArray[0].SomeFunction();  
   baseArray[1].SomeFunction();  
   baseArray[2].SomeFunction();  
   baseArray[3].SomeFunction();  
   baseArray[4].SomeFunction();  
   baseArray[5].SomeFunction();  
   baseArray[6].SomeFunction();  
   baseArray[7].SomeFunction();  
   baseArray[8].SomeFunction();  
   baseArray[9].SomeFunction();  
}
int _tmain(int argc, TCHAR* argv[])
{
   Base* derivedArray[10];

   for( int i = 0; i < 10; ++i ) derivedArray[i] = new Derived;

   MyWonderfulCode(derivedArray);
   return 0;
}
于 2009-06-25T11:47:46.827 回答
2

阿德里安·格里戈尔回答:

如果您使用 std::vector,那么不应该有任何崩溃。

然后因为评论删除了答案:

在这种情况下没关系。会以同样的方式崩溃。

但是 Adrian 基本上是正确的——如果 MyWonderFulCode 采用vector<Base>or ,并且您尝试将 or 传递给它vector<Base>&,那么调用代码将无法编译。因此,我想,它也不会崩溃。不可能从一种向量类型转换为另一种(至少,在没有创建新向量并将派生对象切片的情况下是这样)。vector<Base>*vector<Derived>vector<Derived>*

因此,在这种情况下,您也需要编译失败,因为也无法从一种数组类型转换为另一种数组类型。问题是当作为函数参数传递时,C/C++ 无法区分指针和数组。由于完全合理的隐式向上转换 from Derived*to Base*,我们被隐式向上转换 from Derived[]to所困扰Base[],它几乎总是被破坏。在 C++ 术语中,这是一个完全不合理的reinterpret_cast.

这也是 C++ 程序员不使用数组的原因之一(除非他们必须这样做)。

于 2009-06-25T11:46:42.817 回答
0

让您的 MyWonderfulCode 函数采用 Base指针数组而不是 Base 本身,然后您可以多态地使用该数组。

于 2009-06-25T11:44:53.897 回答