为什么C++可以使用未初始化的成员变量(引用或指针 *NOT 值复制*)来初始化其父类的成员变量

Why can C++ use the uninitialized member variables (reference or pointer *NOT value copy*) to initialize the member variables of its parent class

本文关键字:成员 变量 初始化 NOT 复制 父类 引用 可以使 C++ 为什么 指针      更新时间:2023-10-16

我有两个关于在C++中初始化成员变量的问题。

  1. 为什么当Fruits的构造函数定义为explicit Fruits(std::string* f_n) :f_name_(f_n)时(或者当这些代码中的指针被替换为引用时),代码explicit Apple(const char* name) :Fruits(&_name)工作?

  2. 派生类的成员变量
  3. 是在其子基类的成员变量之后还是之前分配内存?

请参阅下面的代码。

// Apple.h c++
#pragma once
#include<iostream>
#include<cstring>
#include<string>
class Plant {
public:
Plant() {
std::cout << "Plant construction function called." << std::endl;
}
inline void show() {
std::cout << "name:" << s_name_ << std::endl;
}
void Init() {
std::cout << "Init assign value called." << std::endl;
s_name_ = this->getName();
}
virtual ~Plant() {
std::cout << "Plant deconstruction function called." << std::endl;
}
private:
std::string s_name_;
protected:
virtual std::string getName() = 0;
};
class Fruits: public Plant{
public:
explicit Fruits(std::string* f_n) :f_name_(f_n) {
std::cout << "Fruits construction function called."<< std::endl;
}
virtual ~Fruits() {
std::cout << "Fruits deconstruction function called." << std::endl;
}
protected:
std::string getName() override {
std::cout << "getName() function called." << std::endl; 
return *f_name_;
}
private:
std::string* f_name_;
};
class Apple:public Fruits {
private:
std::string _name;
public:
explicit Apple(const char* name) :Fruits(&_name) {
_name = name;
std::cout << "Apple constuction function called."<< std::endl;
Plant::Init();
}
~Apple() {
std::cout << "Apple deconstruction function called." << std::endl;
}
};
// main.cpp
#include<iostream>
#include "Apple.h"
using namespace std;
int main() {
{
cout << "test1:" << endl;
Apple t1("Apple1");
t1.show();
}
cin.get();
return 0;
}

为什么当 Fruit 的构造函数定义为显式 Fruits(std::string* f_n) :f_name_(f_n) 时,代码显式 Apple(const char* name) :Fruits(&_name) 起作用?

您将成员变量的地址传递_name,该变量的类型为std::string,因此它可以很好地编译,并且explicit与此无关。请注意,您在这里玩非常危险的游戏,如果您尝试在构造函数中取消引用该指针Fruits您将获得 UB。

派生类的成员变量

是在其子基类的成员变量之后还是之前分配内存?

您的问题表明您不了解它是如何工作的。派生类是驻留在内存sizeof(typename)的对象,内存包括基对象和派生添加的所有成员变量(如果有)。当为它分配内存时,它会立即分配给整个对象,而不是按它的一部分分配。不同的问题是初始化的顺序 - 虽然内存分配的对象是从基初始化到派生的,所以如果你尝试访问基类构造函数中派生类的对象,你会得到UB,就像你会尝试访问未初始化的对象一样。

因为它只是向Apple添加一个具有不同签名的构造函数。这与水果无关。

1.为什么代码显式 Apple(const char* name) :水果(&_name) 当水果的构造函数定义为显式时工作 水果(std::string* f_n) :f_name_(f_n) (或当这些中的指针 代码替换为参考)?

您的explicit Fruits(std::string* f_n)需要 std::string 上的指针_name是 std::string 类型的 Apple 成员,因此采用_name地址将转换为所需的 std::string*。

    派生类的成员变量
  1. 是在其子基类的成员变量之后还是之前分配内存?

首先初始化父类,在该派生类之后。 所以在你的情况下,在这里传递Fruits(&_name)

explicit Apple(const char* name) :Fruits(&_name) {
_name = name;

表示此时_name未初始化,将在 Fruit 构造函数完成后进行初始化