本文学习自 狄泰软件学院 唐佐林老师的 C++课程
实验1 :临时对象的引入
实验2:避免产生临时对象
实验3:临时对象的生成和声明周期1
实验4:临时对象的生成和声明周期2
实验5:C++编译器对减少临时对象所做的工作
实验6:实验5:C++编译器对减少临时对象所做的工作深入理解
要避免产生临时对象,在C语言中我们警惕是野指针,那么在C++里面我们最应该警惕是野指针和临时对象。
实验1 :临时对象的引入
#include <stdio.h>
class Test {
int mi;
public:
Test(int i) {
mi = i;
}
Test() {
Test(0);//临时对象,作用域只在这一行代码,过了这一行代码 就消失了。
}
void print() {
printf("mi = %d\n", mi);
}
};
int main()
{
Test t;
t.print();
return 0;
}
mhr@ubuntu:~/work/c++$
mhr@ubuntu:~/work/c++$ g++ 23-1.cpp
mhr@ubuntu:~/work/c++$ ./a.out
mi = 976883248
mhr@ubuntu:~/work/c++$
结果并不是我们预期的0,而是一个随机值。
所以上面实验中 Test(0); ,从作用域和生命周期的角度分析,此处定义的临时对象没有任何作用,成员变量的值仍然是随机值。
Test() {
Test(0);//临时对象,作用域只在这一行代码,过了这一行代码 就消失了。
}
等价于
Test() {
// Test(0);//临时对象,作用域只在这一行代码,过了这一行代码 就消失了。
}
在C语言中我们警惕是野指针,那么在C++里面我们最应该警惕是野指针和临时对象
实验2:避免产生临时对象
#include <stdio.h>
class Test {
int mi;
/*
init()就是一个普通的函数,并不是构造函数,所以调用init()不会生成临时对象。
*/
void init(int i)
{
mi = i;
}
public:
Test(int i) {
init(i);
}
Test() {
init(0);
}
void print() {
printf("mi = %d\n", mi);
}
};
int main()
{
Test t;
t.print();
return 0;
}
mhr@ubuntu:~/work/c++$ g++ 23-2.cpp
mhr@ubuntu:~/work/c++$ ./a.out
mi = 0
mhr@ubuntu:~/work/c++$
实验3:临时对象的生成和声明周期1
#include <stdio.h>
class Test {
int mi;
/*
init()就是一个普通的函数,并不是构造函数,所以调用init()不会生成临时对象。
*/
void init(int i)
{
mi = i;
}
public:
Test(int i) {
printf("Test(int i)\n");
init(i);
}
Test() {
printf("Test()\n");
init(0);
}
void print() {
printf("mi = %d\n", mi);
}
~Test() {
printf("~Test()\n");
}
};
int main()
{
printf("main begin\n");
Test();//直接调用构造函数,将产生一个临时对象,马上构造马上析构
Test(1);//直接调用构造函数,将产生一个临时对象,马上构造马上析构
printf("main end\n");
return 0;
}
mhr@ubuntu:~/work/c++$
mhr@ubuntu:~/work/c++$ g++ 23-1.cpp
mhr@ubuntu:~/work/c++$ ./a.out
main begin
Test()
~Test()
Test(int i)
~Test()
main end
mhr@ubuntu:~/work/c++$
实验4:临时对象的生成和声明周期2
#include <stdio.h>
class Test {
int mi;
/*
init()就是一个普通的函数,并不是构造函数,所以调用init()不会生成临时对象。
*/
void init(int i)
{
mi = i;
}
public:
Test(int i) {
printf("Test(int i)\n");
init(i);
}
Test() {
printf("Test()\n");
init(0);
}
void print() {
printf("mi = %d\n", mi);
}
~Test() {
printf("~Test()\n");
}
};
int main()
{
printf("main begin\n");
Test().print();//直接调用构造函数,将产生一个临时对象,在通过临时对象调用成员函数,最后析构
Test(1).print();//直接调用构造函数,将产生一个临时对象,在通过临时对象调用成员函数,最后析构
printf("main end\n");
return 0;
}
mhr@ubuntu:~/work/c++$
mhr@ubuntu:~/work/c++$ g++ 23-1.cpp
mhr@ubuntu:~/work/c++$ ./a.out
main begin
Test()
mi = 0
~Test()
Test(int i)
mi = 1
~Test()
main end
mhr@ubuntu:~/work/c++$
实验5:C++编译器对减少临时对象所做的工作
#include <stdio.h>
class Test
{
int mi;
public:
Test(int i)
{
printf("Test(int i) : %d\n", i);
mi = i;
}
Test(const Test& t)
{
printf("Test(const Test& t) : %d\n", t.mi);
mi = t.mi;
}
Test()
{
printf("Test()\n");
mi = 0;
}
int print()
{
printf("mi = %d\n", mi);
}
~Test()
{
printf("~Test()\n");
}
};
Test func()
{
return Test(20);
}
int main()
{
/*
Test t = Test(10) ==> Test t(10) ==> Test t = 10
Test t = Test(10);//1. 生成临时对象 2.用临时对象初始化(调用拷贝构造函数 )
*/
Test t = Test(10);
t.print();
return 0;
}
mhr@ubuntu:~/work/c++$ g++ 23-3.cpp
mhr@ubuntu:~/work/c++$ ./a.out
Test(int i) : 10
mi = 10
~Test()
mhr@ubuntu:~/work/c++$
问题1:结果显示并没有调用拷贝构造函数啊,这是为什么呢?
答案1:这是因为现代 C++编译器在不影响最终结果的前提下,会尽力的减少临时对象的产生,编译器直接将
Test t = Test(10);
替换为
Test t = 10
从而杜绝了临时对象的产生。
问题2:为什么C++编译器要杜绝临时对象的产生呢?
答案2:由上面的例子可以看出 如果编译器按照原生程序的写法 Test t = Test(10); 来执行程序的话,需要调用两次构造函数再加上一次拷贝构造函数,效率会远远低于 Test t = 10;的写法,只调用一次构造函数。
实验6:实验5:C++编译器对减少临时对象所做的工作深入理解
#include <stdio.h>
class Test
{
int mi;
public:
Test(int i)
{
printf("Test(int i) : %d\n", i);
mi = i;
}
Test(const Test& t)
{
printf("Test(const Test& t) : %d\n", t.mi);
mi = t.mi;
}
Test()
{
printf("Test()\n");
mi = 0;
}
int print()
{
printf("mi = %d\n", mi);
}
~Test()
{
printf("~Test()\n");
}
};
Test func()
{
return Test(20);
}
int main()
{
Test t = Test(10); // ==> Test t = 10;
Test tt = func(); // ==> Test tt = Test(20); ==> Test tt = 20;
t.print();
tt.print();
return 0;
}
mhr@ubuntu:~/work/c++$
mhr@ubuntu:~/work/c++$ g++ 23-3.cpp
mhr@ubuntu:~/work/c++$ ./a.out
Test(int i) : 10
Test(int i) : 20
mi = 10
mi = 20
~Test()
~Test()
mhr@ubuntu:~/work/c++$