程序转化语义
- 我们写的代码,编译器会对代码进行拆分,拆分成编译器更容易理解和实现的代码。
- 看一看编译器是如何解析这些代码的。
- 站在程序员角度/站在编译器角度
- 程序员看代码视角 和 编译器看代码视角之间不断切换。
定义时初始化对象
class X { public: int m_i; X(const X& tmpx) { m_i = tmpx.m_i; cout << "拷贝构造函数被调用" << endl; } X() { m_i = 0; cout << "构造函数被调用" << endl; } }; int main() { X x0; x0.m_i = 15; X x1 = x0; // 定义的时候初始化,调用拷贝构造函数 X x2(x0); // 定义的时候初始化,调用拷贝构造函数 X x3 = (x0); // 定义的时候初始化,调用拷贝构造函数 }
- 输出结果:
- 切换到编译器角度,编译器会拆分成两个步骤(编译器视角)
- X x3; 步骤一:定义一个对象,为对象分配内存。从编译器视角来看,这句是不调用X类的构造函数
- x3.X::X(x0); 步骤二:直接调用对象的拷贝构造函数去了
- 总结:
- 编译器先创建对象,再调用对象的拷贝构造函数进行拷贝
参数的初始化
class X { public: int m_i; X(const X& tmpx) { m_i = tmpx.m_i; cout << "拷贝构造函数被调用" << endl; } X() { m_i = 0; cout << "构造函数被调用" << endl; } ~X() { cout << "析构函数被调用" << endl; } }; void func(X tmpx) { return; } int main() { X x0; func(x0); return 1; }
- 输出结果
- 程序员角度:在传参时,调用拷贝构造函数将x0拷贝给tmpx,函数执行完毕,析构tmpx。
- 现代编译器角度:函数先创建tmpx对象,然后调用该对象的拷贝构造函数将x0拷贝给tmpx。类似于 X tmpx(x0)的执行过程。
老编译器视角
- X tmpobj; 编译器产生一个临时对象
- tmpobj.X::X(x0); 调用拷贝构造函数
void func(X tmpx) { return; } 老编译器看func(老编译器角度) void func(X &tmpx) { return; }
- func(tmpobj); 用临时对象调用func
- tmpobj.X::~X(); func()被调用完成后,本析构被调用
- 归纳:老编译器先在外面将临时对象构造出来,再以引用的方式传给函数(将传值方式改为引用)。
返回值初始化
class X { public: int m_i; X(const X& tmpx) { m_i = tmpx.m_i; cout << "拷贝构造函数被调用" << endl; } X() { m_i = 0; cout << "构造函数被调用" << endl; } ~X() { cout << "析构函数被调用" << endl; } }; X func() { X x0; //.... return x0; } int main() { X my = func(); return 1; }
- 输出结果(程序员视角解析):
- 编译器对上述代码的理解(编译器角度)
- X my; 只是生成对象my,不会调用X的构造函数
- func(my);
- 编译器角度的func
void func(X &extra) { X x0; //从编译器角度,这里不调用X的构造函数 //... //... extra.X::X(x0); //调用拷贝构造函数,将x0拷贝给外部传来的引用 return; }
给类增加一个成员函数
class X { public: int m_i; X(const X& tmpx) { m_i = tmpx.m_i; cout << "拷贝构造函数被调用" << endl; } X() { m_i = 0; cout << "构造函数被调用" << endl; } ~X() { cout << "析构函数被调用" << endl; } void functest() { cout << "functest()被调用" << endl; } }; X func() { X x0; //.... return x0; } int main() { func().functest(); return 1; }
- 输出结果:
- 程序员视角:
- func().functest();
- 编译器视角
- X my; 不会调用X的构造函数
- (func(my), my).functest(); 逗号表达式:先计算表达式1,再计算表达式2,整个逗号表达式的结果是表达式2的值;
- 其中(func(my)为:
void func(X &extra) { X x0; //从编译器角度,这里不调用X的构造函数 //... //... extra.X::X(x0); //调用拷贝构造函数,将x0拷贝给外部传来的引用 return; }