Skip to content

Latest commit

 

History

History
303 lines (245 loc) · 4.9 KB

06.程序转换语义.md

File metadata and controls

303 lines (245 loc) · 4.9 KB

程序转换语义

程序员写的代码,编译器在处理的时候会进一步把这些代码拆分,拆分成更容易被理解和实现的代码

定义时初始化对象

#include <iostream>
using namespace std;

class X
{
public:
	int m_i;
	X(const X& tmpx) {
		m_i = tmpx.m_i;
		cout << "X类的拷贝构造函数被调用" << endl;
	}
	X() {
		m_i = 0;
		cout << "X类的构造函数被调用" << endl;
	}
};

int main()
{
	X x0;
	x0.m_i = 15;
	X x1 = x0;
	X x2(x0);
	// 分析这一行
	X x3 = x0;

	return 0;
}
X类的构造函数被调用
X类的拷贝构造函数被调用
X类的拷贝构造函数被调用
X类的拷贝构造函数被调用

以编译器的角度看这行代码

X x3 = x0;

切换到编译器角度,编译器会拆分成两个步骤(编译器视角)

  • X x3; 步骤一:定义一个对象,为对象分配内存。从编译器视角来看,这句是不调用X类的构造函数
  • x3.X::X(x0); 步骤二:直接调用对象的拷贝构造函数去了

总结:

编译器先创建对象,再调用对象的拷贝构造函数进行拷贝

参数的初始化

#include <iostream>
using namespace std;

class X
{
public:
	int m_i;
	X(const X& tmpx) {
		m_i = tmpx.m_i;
		cout << "X类的拷贝构造函数被调用" << endl;
	}
	X() {
		m_i = 0;
		cout << "X类的构造函数被调用" << endl;
	}
	~X() {
		cout << "X类的析构函数被调用" << endl;
	}
};

void func(X tmpx)
{
	return;
}

int main()
{
	X x0;
	func(x0);

	return 0;
}
X类的构造函数被调用
X类的拷贝构造函数被调用
X类的析构函数被调用
X类的析构函数被调用

站在编译器的视角

func(x0);

编译器是在func这个函数的空间内构造出来了tmpx对象,然后在func函数返回前,把tmpx对象析构掉

一些老编译器的视角中

//以下这些可以认为是伪代码,并不代表在当代编译器上能够正确运行,只是借此了解编译稽内部具体都做了什么
X tmpobj; 		//编译然产生出来一个临时对象
tmpobj.X::X(x0);//调用拷贝构造函数
func(tmpobj); 	//用临时对象调用func
tmpobj.X::~X();	//func()被调用完成后,析构函数被调用

func函数的参数站在老编译器的角度也会做出调整,变成引用

void func(X &tmpx)
{
    return;
}

返回值初始化

#include <iostream>
using namespace std;

class X
{
public:
	int m_i;
	X(const X& tmpx) {
		m_i = tmpx.m_i;
		cout << "X类的拷贝构造函数被调用" << endl;
	}
	X() {
		m_i = 0;
		cout << "X类的构造函数被调用" << endl;
	}
	~X() {
		cout << "X类的析构函数被调用" << endl;
	}
};

X func()
{
	X x0;
	return x0;
}

int main()
{
	X my = func();
    // X my;
    // my.func();

	return 0;
}
X类的构造函数被调用
X类的拷贝构造函数被调用
X类的析构函数被调用
X类的析构函数被调用

站在编译器的视角下

void func(X &extra)	//1.安插一个引用参数
{
    X x0;			//2.调用构造函数x0.X::X()
    //...
    extra.X::X(x0);	//3.return之前安插一个对拷贝构造函数的调用
    //return x0;
    return;
}

看见了吧,并不是说直接在函数外复制了一个,而是通过改变函数形参,先引用进来,然后再对得到的别名进行拷贝构造函数。之前认为是在()里完成的,但其实编译器是先引用再在函数体内执行拷贝构造函数。

编译器看main函数中代码

X my;		// 从编译器的视角看,着里是不调用构造函数的,除非编译器主动增加代码my.X::X();
func(my);

最终

#include <iostream>
using namespace std;

class X
{
public:
	int m_i;
	X(const X& tmpx) {
		m_i = tmpx.m_i;
		cout << "X类的拷贝构造函数被调用" << endl;
	}
	X() {
		m_i = 0;
		cout << "X类的构造函数被调用" << endl;
	}
	~X() {
		cout << "X类的析构函数被调用" << endl;
	}
};

void func(X& extra)
{
	X x0;
    extra.X::X(x0);
	return;
}

int main()
{
	X my;
	func(my);
    
	return 0;
}

如果增设一个public函数

#include <iostream>
using namespace std;

class X
{
public:
	int m_i;
	X(const X& tmpx) {
		m_i = tmpx.m_i;
		cout << "X类的拷贝构造函数被调用" << endl;
	}
	X() {
		m_i = 0;
		cout << "X类的构造函数被调用" << endl;
	}
	~X() {
		cout << "X类的析构函数被调用" << endl;
	}
	void functest() {
		cout << "functest()成员函数被调用" << endl;
	}
};

X func()
{
	X x0;
	return x0;
}

int main()
{
	X my = func();
	func().functest(); 

	return 0;
}

编译器视角下的main函数

X my;
(func(my), my).functest(); //逗号运算符,先求解表达式1,再求解表达式2,整个表达式的值是表达式2的值

再增加如下代码

X(*pf);	//定义函数指针
pf = func;
pf().functest();

编译器的视角

X my;
void (*pf)(X&);
pf = func;
pf(my);
my.functest();