将派生类成员传递给基类构造函数



我有以下一段代码正在工作,但我不明白为什么它能工作(灵感来自现实生活中的代码库(:

基类定义:

class Pointers {
private:
int* Obj1;
double* Obj2;
public:
Pointers(int* Obj1_, double* Obj2_) : Obj1{Obj1_}, Obj2{Obj2_} {}
};

现在,我们从基类派生一个类,在基类中,我们用同名的intdouble对两个指针进行阴影处理:

class Objects : public Pointers {
public:
int Obj1{69};
double Obj2{72};
Objects() : Pointers(&Obj1, &Obj2) {}
};

Objects的构造函数中,我们调用Pointers的构造函数,并传递Obj1Obj2的地址。这实际上是有效的:(阴影(指针将指向Obj1 (69)Obj2 (72)

我的问题是:为什么这样有效?我认为在第一步中构造基类成员(它们是两个指针(,然后才构造派生类成员(具有相同名称的intdouble(。我们如何首先将这些对象地址传递给基类构造函数?

它是有效的,因为当您调用基类构造函数时,派生类成员

int Obj1{69};
double Obj2{72};

尚未初始化,但它们的地址是已知的,并且您正在使用它们的地址初始化基类中的指针。请注意,它与阴影几乎没有关系。

当然,如果您尝试在Pointers构造函数中打印指向值

Pointers(int* Obj1_, double* Obj2_) : Obj1{Obj1_}, Obj2{Obj2_} {
std::cout << "Obj1: " << *Obj1 << ", Obj2: " << *Obj2 << std::endl;
}

您将得到一些垃圾(UB(,因为指向的值尚未初始化。为了使代码更加清晰,避免在类主体中分配成员字段,而是使用构造函数成员初始值设定项列表:

Objects() : Pointers(&Obj1, &Obj2), Obj1{69}, Obj2{72} {}

类的初始化实际上如下进行:

  1. 确定用于构造对象的内存(例如堆栈位置或使用new运算符的结果(
  2. 输入Object::Object()的调用
  3. Pointers::Pointers(int* Obj1_, double* Obj2_)被调用这发生在执行Object成员的任何初始化程序之前内存位置已经可用(请参见步骤1.(,因此可以使用操作员的地址
  4. 对象的Pointers部分初始化完成
  5. Object的成员变量被初始化

简单地注意,可以简单地传递成员变量的地址,但您需要确保指针在基类构造函数中没有被取消引用,因为这会导致未定义的行为。

此处:

class Objects : public Pointers {
public:
int Obj1{69};
double Obj2{72};
Objects() : Pointers(&Obj1, &Obj2) {}
};

CCD_ 18是指CCD_。如果在Pointers中存在相同的命名成员,则它被Objects中的成员遮蔽。您需要限定名称Pointers::Obj1。你不这么做,所以Obj1指的是Objects::Obj1。这里没什么奇怪的。

然后在这里:

class Pointers {
private:
int* Obj1;
double* Obj2;
public:
Pointers(int* Obj1_, double* Obj2_) : Obj1{Obj1_}, Obj2{Obj2_} {}
};

没有任何东西被遮蔽,Obj1_Obj1是不同的标识符。基类使用传递给构造函数的指针初始化其成员。

这似乎表明,当指针的ctor在对象的ctor中被调用时,派生类的成员将位于内存中的哪个地址是已知的?

是。在初始化正在构造的对象的成员之前,可以使用这些成员的地址和引用。只要不取消引用指针(或尝试通过引用读取成员的值(,一切都很好。例如,如果在基本构造函数主体中添加一些std::cout << *Obj1;,则代码将调用未定义的行为,因为Obj指向的int此时未初始化。

最新更新