派生类的一些问题

Some trouble with derived classes

本文关键字:问题 派生      更新时间:2023-10-16

我试图实现下面的函数和类,但输出为arr[I]。X错了。我正确地得到arr[0]。X = 0, arr[1]。X = 0,但arr[2]。X不返回0。知道为什么吗?

class Base {
public:
  int x;
};
class Derived : public Base {
public:
int y;
void init(Base *b);
void foo();
};
void Derived :: init(Base *b) {
  for( int i = 0; i < 3; ++i) {
    b[i].x = 0;
  }
}
void Derived :: foo() { 
  Derived arr[3];
  init(arr);
  for( int i = 0; i < 3; ++i) {
  cout<<"b["<<i<<"] "<<arr[i].x;
  }
}

int main()
{
    Derived der;
    der.foo();
    return 0;
}
void Derived :: foo() { 
    Derived arr[3];
    init(arr);

指针指向Derived, Derived*,传递给函数init(Base *b)。接下来发生的事情是,不是通过表中的sizeof(Derived)=8移动,您的函数将通过sizeof(Base)=4移动,这导致数组中第一个和第二个Derived以及第一个Derivedyx成员初始化,但不是Derivedx

指针的算术运算是基于对象类型的大小完成的指针

考虑以下内存布局(在x64上):

派生:

: foo ():

Derived arr[3];
0x7fffffffe310  // 1st Derived, also address of arr[0].x
+8 = 
0x7fffffffe318  // 2nd Derived, also address of arr[1].x
+8 = 
0x7fffffffe320  // 3rd Derived, also address of arr[2].x

但是在Derived::init(Base* b):

b[0].x = 0x7fffffffe310  // set arr[0].x to 0
+4 =
b[1].x = 0x7fffffffe314  // set arr[0].y to 0
+4 =
b[2].x = 0x7fffffffe318  // set arr[1].x to 0

因此,您设置了arr[0]。X到0,arr[1]。X到0,顺便说一下arr[0]。Y到0。这不是你想要的。解决方法是将Derived::init改为

void Derived::init( Derived *d) {
  for( int i = 0; i < 3; ++i) {
    d[i].x = 0;
  }
}

或者更好,遵循更泛型编程的原则:

template < size_t N>
void Derived::init( Derived (&d)[N] ) {
    for( int i = 0; i < N; ++i) {
        d[i].x = 0;
    }
}

原因是init()不知道您实际上传递的是Derived*而不是Base*。

在init()函数的循环中,假设要到达数组中的下一项,sizeof(Base)被添加到指针中,而不是sizeof(Derived)

change

cout<<"b["<<i<<"] "<<arr[i].x;

cout<<"b["<<i<<"] "<<arr[i].x<<endl;

然后你会看到正确的数字。

endl的额外功能是刷新与该流相关的缓冲区。如果不这样做,它有时会输出一些随机数。

首先,你通过值传递指针(*b)给函数init(),而不是通过引用。

这里有一个很好的解释为什么这很重要,他们解释得比我更好

试着让init函数看起来像这样

init(Base& *b)