考虑以下程序:
#include <string>
#include <iostream>
class B {
private:
std::string s;
public:
B() { s = fun(); }
std::string fun() { return "hello"; }
void print() {
std::cout << s;
}
};
int main(){
B b;
b.print();
}
输出为Hello
我的问题是:
我的疑问是我如何在b
对象上调用尚未由构造函数创建的函数。
答案 0 :(得分:5)
在对象的构造函数主体开始执行时,对象的所有基础(直接的或间接的)以及成员都已被初始化,无论是显式还是隐式的。因此,s
是有效的字符串对象,从法律上讲,它可以是赋值的LHS。
在这里可能应该注意到的一件事情是,如果您从构造函数中调用了多态类的虚拟方法,那么将选择当前类型的实现,因为尚未对任何派生类型进行初始化,因此其重载(如果有的话)打电话是非法的。
答案 1 :(得分:0)
- 按顺序为数据成员分配内存(在这种情况下为's')。
对象始终存储在无法调整大小的连续内存块中。因此,在分配超对象时,所有子对象都将同时分配。
请注意,像std::string
这样的对象也可以分配一个与该对象本身的内存分开的动态内存缓冲区。可以分配此内存的最早时间是在字符串的构造函数中。也就是说,空字符串不需要缓冲区,因此可以将分配延迟到以后。
- 在构造函数中调用fun()时对象是否存在。
不。该对象还不完全存在。它的所有内存都在构造函数之前,而所有子对象将在该对象的构造函数之前完全构造。但是对象的生命周期是在构造函数完成后开始的。那说:
我的疑问是如何在尚未由构造函数创建的b对象上调用函数。
可以在构造函数中调用成员函数。
您只需要确保不要执行任何依赖于已执行的构造函数的操作,因为尚未执行该构造函数。例如,如果您指定由构造函数建立的类不变式,则在建立不变式之前,您不得调用任何依赖于该不变式的函数。
请注意,成员函数也可以从成员初始化列表中调用。那里,一些子对象尚未初始化,因此必须非常小心,不要调用将访问未初始化子对象的成员函数。
答案 2 :(得分:0)
- 按顺序为数据成员分配内存(在这种情况下为's')。
- 在构造函数中调用fun()时对象是否存在。
要回答您的问题,请让我们参考CPP标准(N4713)。 请参见下面突出显示的部分:
15.6.2初始化基础和成员
...
13.在非委托构造函数中,初始化按以下顺序进行:
(13.1)—首先,并且仅对于最派生类(6.6.2)的构造函数,将虚拟基类按照它们在基的有向无环图的深度优先从左到右遍历时出现的顺序进行初始化类,其中“从左到右”是基类在派生类base-specifier-list中的出现顺序。
(13.2)—然后,直接基类按照它们出现在base-specifier-list中的声明顺序进行初始化(与mem-initializer的顺序无关)。
(13.3)— 然后,非静态数据成员按照它们在类定义中声明的顺序进行初始化(同样,无论mem-initializer的顺序如何)。
(13.4)— 最后,执行构造函数主体的复合语句。
(13.3)回答1.和(13.4)回答2.到输入构造函数主体时,所有非静态数据成员都将初始化。
答案 3 :(得分:0)
我的疑问是如何在尚未由构造函数创建的b对象上调用函数。
已创建 。
所有成员都是在构造函数主体开始之前创建的。
否则,构造函数主体将毫无用处!
从哲学上讲,这就是为什么我们永远不应该说B b
“调用构造函数”的原因(这在创建B()
之类的临时文件时尤其重要, 看起来像调用一个函数/构造函数)…因为,尽管显然涉及到构造函数,但也发生了其他事情,其中一些/大部分发生在首先。