建立一个类A,首先实例化一个对象x
A x;
下面有三种显示的初始化操作(也就是说在定义的时候就进行初始化操作)
A x1 = x;
A x2(x);
A x3 = (x);
那么对于我们来说,可以理解为在实例化对象的时候通过拷贝构造函数进行了拷贝操作,那么实际上在编译器层面看来,其实将上面的一句代码分成了两句。
A x1; // 这里没有调用默认构造函数
x1.A::A(x);
但是对于第一句的实例化对象来说,站在编译器的角度来看不会主动的去调用构造函数,所以这里没有去调用默认构造函数,而是在第二行调用了拷贝构造函数进行拷贝操作。
参数的初始化操作是指将对象作为函数的参数进行传递,先来看一下下面的这个代码:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout << "默认构造函数" << endl;
}
A(const A&) {
cout << "拷贝构造函数" << endl;
}
~A() {
cout << "析构函数" << endl;
}
};
void func(A x) {
return ;
}
int main()
{
A x;
func(x);
return 0;
}
执行结果为:
默认构造函数 拷贝构造函数 析构函数 析构函数
在之前学习类的时候就知道会是这样的输出的一种情况,实际上在以前的老版本的编译器中所运行的代码实际上是这样的:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout << "默认构造函数" << endl;
}
A(const A&) {
cout << "拷贝构造函数" << endl;
}
~A() {
cout << "析构函数" << endl;
}
};
void func(A &x) {
return ;
}
int main()
{
A x;
A tmpx;
tmpx.A::A(x);
func(tmpx);
tmpx.A::~A();
return 0;
}
是在函数外面实例化一个临时对象然后作为引用传到函数中去,在函数结束的时候析构,但是这是在老版本的编译器中是这样的,毕竟深度探索C++对象模型这本书也比较久远了,所以现在的编译器还是直接通过显示的初始化进行操作的,也就是先实例化一个对象,然后再调用拷贝构造函数,在函数内的对象也仅限于函数内使用。
返回值初始化是指一个函数返回一个类对象的初始化操作,我们一般写的代码是下面这样的:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout << "默认构造函数" << endl;
}
A(const A&) {
cout << "拷贝构造函数" << endl;
}
~A() {
cout << "析构函数" << endl;
}
};
A func() {
A x1;
//...
return x1;
}
int main()
{
A x = func();
return 0;
}
这个很好理解,在函数中实例化一个对象,然后将其返回并拷贝给x对象。而在编译器的视角来看,它用了一种很巧妙的方法来实现这个操作,先来看一下编译器的处理代码:
#include <iostream>
using namespace std;
class A {
public:
A() {
cout << "默认构造函数" << endl;
}
A(const A&) {
cout << "拷贝构造函数" << endl;
}
~A() {
cout << "析构函数" << endl;
}
};
void func(A &x) { // 返回值为空, 参数为引用
A x1; // 这里不会调用默认构造函数
//... 做一些操作
x.A::A(x1); // 在这里将x1拷贝给x
return ;
}
int main()
{
A x;
func(x);
return 0;
}
这个操作就是将主函数中的x对象的地址传进去,然后在函数中实例化一个临时对象并进行一系列操作后,再拷贝构给x对象,那么实际上就是将结果拷贝给了主函数中的x对象。
有时候我们在主函数中不需要实例化一个对象,而去调用func函数返回的对象直接调用该对象的成员函数,比如下面的这个代码:
A func() {
A x1;
//... 做一些操作
return x1;
}
int main()
{
func().Afunc(); // Afunc为成员函数
return 0;
}
那么在编译器看来,这个代码是这样的,实现方法也很巧妙:
void func(A &x) {
A x1;
//... 做一些操作
x1.A::A(x); // 将x1拷贝给x
return ;
}
int main()
{
A x; // 不会调用默认构造函数
(func(x),x).Afunc(); // Afunc为成员函数
x.A::~A();
return 0;
}
这个方法巧妙就巧妙在(func(x), x).Afunc();这句,这句代码前面使用了逗号表达式,也就是说func(x)使x有了一些值,然后(func(x), x)的值就是x,然后再去调用x.Afunc()。
如果我们要使用函数指针的形式,比如下面的形式:
A func() {
A x1;
//... 做一些操作
return x1;
}
int main()
{
A(*pf)();
pf = func;
pf().Afunc();
return 0;
}
那么编译器的视角就是下面这个样子:
void func(A &x) {
A x1;
//... 做一些操作
x.A::A(x1); // 将x1拷贝给x
return ;
}
int main()
{
A x; // 不调用默认构造函数
void (*pf)(A&);
pf = func;
pf(x);
x.Afunc();
x.A::~A();
return 0;
}