Program transformation semantics
- For the code we write, the compiler will split the code into code that is easier for the compiler to understand and implement.
- Take a look at how the compiler parses these codes.
- From the programmer's perspective/from the compiler's perspective
- The programmer's view of code and the compiler's view of code are constantly switching.
Initialize object when defined
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); // 定义的时候初始化,调用拷贝构造函数 }
- Output result:
- Switch to the compiler perspective, the compiler will be split into two steps (compiler perspective)
- X x3; Step 1: Define an object and allocate memory for the object. From the perspective of the compiler, this sentence does not call the constructor of the X class
- x3.X::X(x0); Step 2: Call the copy constructor of the object directly
- to sum up:
- The compiler first creates the object, and then calls the object's copy constructor to copy
Initialization of parameters
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; }
- Output result
- Programmer's point of view: When passing parameters, call the copy constructor to copy x0 to tmpx. After the function is executed, tmpx is destructed.
- From a modern compiler perspective: the function first creates a tmpx object, and then calls the object's copy constructor to copy x0 to tmpx. Similar to the execution process of X tmpx(x0).
Old compiler perspective
- X tmpobj; The compiler generates a temporary object
- tmpobj.X::X(x0); call the copy constructor
void func(X tmpx) { return; } 老编译器看func(老编译器角度) void func(X &tmpx) { return; }
- func(tmpobj); call func with a temporary object
- tmpobj.X::~X(); After func() is called, the destructor is called
- Summary: The old compiler first constructs the temporary object outside, and then passes it to the function by reference (change the value passing method to reference).
Return value initialization
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; }
- Output result (analysis from programmer's perspective):
- The compiler's understanding of the above code (compiler perspective)
- X my; just generates the object my, will not call the constructor of X
- func(my);
- Func from the compiler perspective
void func(X &extra) { X x0; //从编译器角度,这里不调用X的构造函数 //... //... extra.X::X(x0); //调用拷贝构造函数,将x0拷贝给外部传来的引用 return; }
Add a member function to the class
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; }
- Output result:
- Programmer's perspective:
- func (). functest ();
- Compiler perspective
- X my; will not call X's constructor
- (func(my), my).functest(); Comma expression: first calculate expression 1, then expression 2, the result of the entire comma expression is the value of expression 2;
- Where (func(my) is:
void func(X &extra) { X x0; //从编译器角度,这里不调用X的构造函数 //... //... extra.X::X(x0); //调用拷贝构造函数,将x0拷贝给外部传来的引用 return; }