在C++中如何使用多态创建数组?

8
class Base1
{
    private:
     int testInput; 
    public:
       Base1();
       virtual int GetRow(void) = 0;
 };

 Base1::Base1()
 {
   testInput = 0;
 }

class table : public Base1
{
   private:
    int row;    
   public:  
     table();
     virtual int GetRow(void);
};

table::table()
{   
  //Contructor
  row = 5;
}

int table::GetRow()
{
  return row;
}

int main ()
{
  Base1* pBase = new table[3];
  pBase[0].GetRow();
  pBase[1].GetRow();   //when i get to  this line, the compiler keep saying access
                           // violation.
  pBase[2].GetRow();

  return 0;
}

我正在尝试创建一个包含3个表类的数组。要求使用Base对象完成。

Base1 * pBase = new table[3];  

在我看来,这段代码没问题。但是当我试图访问每个表格时,编译器却说它有访问违规的问题。虽然我使用的是Visual Studio 2010,但我不知道这段代码错在了哪里。

4个回答

18

在C++中,多态和数组不兼容。

由于通常派生类的大小与基类的大小不同,所以多态和指针算术无法很好地协作。由于数组访问涉及指针算术,因此像 pBase[1] 这样的表达式不能按预期工作。

一种可能的解决方案是有一个指向对象的指针数组,甚至可以使用智能指针简化内存管理。但不要忘记在 Base1 中定义虚析构函数。


6

您之所以会遇到错误,是因为该数组被静态类型化为Base1。这意味着这行代码:

pBase[1].GetRow();

Base1的大小以字节为单位添加到pBase,并将其解释为另一个Base1对象的开头,但实际上它指向第一个table实例的中间位置。

如果您需要多态实例的数组,则必须通过指针(或更好地使用某种智能指针)将它们存储在数组(或更好地存储在std::vector)中。


5

Agnew的回答很到位。让我再解释一下。通过增强你的代码,我打印出了一个Base1和一个table对象的大小以及三个table对象的地址,它们是由new运算符创建的:

A Base1 object is 8 bytes
A table object is 12 bytes
A table object is being constructed at 0x002977C0
A table object is being constructed at 0x002977CC
A table object is being constructed at 0x002977D8

如您所见,这些对象在内存中相互间隔12个字节。

现在,让我们打印出pBase[0]、pBase[1]和pBase[2]所给出的地址:

pBase[0] is at 0x002977C0
pBase[1] is at 0x002977C8
pBase[2] is at 0x002977D0

现在看一下发生了什么:我们获得的指针相互间隔8个字节。这是因为指针运算是在类型为 Base1 的指针上完成的,而由于 Base1 长度为8字节,编译器所做的就是将 pBase[n] 转换为 pBase + (n * sizeof(Base1))。现在你应该能够理解为什么第一个 GetRow() 起作用,以及为什么第二个会导致崩溃。

而且,嘘...但将其转换为正确的类型,然后在转换后的类型上调用方法将防止崩溃。 - johnathan
你的意思是 ((table)pBase)[1].GetRow() 吗?这样做虽然可行,但是这是一件非常糟糕的事情,几乎肯定会给你带来麻烦。 - Nik Bougalis
是的,我确实是指这个。是的,这很“可怕”,但这是一种有效的方法,可以诱导编译器以正确的地址访问多态对象。 - johnathan
1
谢谢大家,现在我对多态有更深入的理解了。 - Kaleidoscope
@NikBougalis: ((table)pBase)[1].GetRow() 不保证可行 *(即使修复了拼写错误)*。你的意思是 dynamic_cast<table*>(pBase)[1].GetRow()?那样就可以了。 - Nawaz
“oops - 感谢您发现了拼写错误。dynamic_cast<>与我展示的C样式转换做了非常不同的事情,其最接近的C++等效形式将是static_cast<table*>(pBase)[1].GetRow()。这个(已更正的)C样式转换在这种情况下被保证能够正确工作,但正如我所指出的那样,它很糟糕,几乎肯定会让人后悔。” - Nik Bougalis

2
您需要使用new关键字来创建所有的数组元素,操作如下:
for(int i = 0; i < 3; ++i)
    pBase[i] = new table();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接