引言
函数对象,以及其构造函数等,在C++中经常用,所以这篇文章就分析一下其中的调用以及如何优化
函数在使用过程调用了哪些方法
#include<iostream> using namespace std; class Test { public: Test(int a = 10) : ma(a) { cout << "Test(int)" << endl; } ~Test() { cout << "~Test()" << endl; } Test(const Test& t) :ma(t.ma) { cout << "Test(const Test&)" << endl; } Test& operator=(const Test& t) { cout << "operator=" << endl; ma = t.ma; return *this; } private: int ma; }; int main() { Test t1; Test t2(t1); Test t3 = t1; // Test(20)是显式生成临时对象,生存周期为所在的语句 /* C++编译器对于对象构造的优化:用临时对象生成新对象时,临时对象就不产生了,直接构造新对象就行 */ Test t4 = Test(20);// Test t4(20) 没有任何区别 cout << "------------" << endl; t4 = t2; // t4.operator = (const Test & t); // 显式生成临时对象 /* 因为这时候t4已经有了,所以再用临时对象的话,临时对象就要产生 */ t4 = Test(30); t4 = (Test)30; // 隐式生成临时对象 t4 = 30; // Test(30) int->Test(int) cout << "------------" << endl; return 0; }
运行结果为:
t4 = Test(30);是先临时生成一个对象所以调用了Test(int),这个临时对象再赋值给t4,所以调用了operator=,临时对象再析构,所以是~Test()
下面这三行代码,都是
Test(int)
operator=
~Test()
// 显式生成临时对象 t4 = Test(30); t4 = (Test)30; // 隐式生成临时对象 t4 = 30;
只不过前两个是显式,因为指明了Test,只不过从Test里面找int,最后一个是隐式,推断t4的类Test,再通过30这个int,找Test里面的int
前两个没有任何区别
这时再在上面的代码最后再加上这样的代码
p指向的是一个已经析构的临时对象 Test *p = &Test(40); const Test &ref = Test(50);
此时输出会显示为
Test(int) 临时对象产生 ~Test() 临时对象析构,当Test *p = &Test(40);这一句执行后,就被析构,所以此时指针已经无效了,因为临时对象已经被析构 Test(int) 引用的临时对象 ~Test() 当main函数结束时,被析构
临时对象是没有名字的,所以过了这一句的生命周期,用指针指的话,就为空了
但是引用相当于别名,是把这块内存安了个名字,在这里,只有当引用对象什么时候出作用域,它才被析构
所以用指针指向临时对象是不安全的
再看两个
Test *p1 = new Test(10); // Test(int) 此时不是临时对象因为用的new,在堆上分配,而上面的是在栈上 Test *p1 = new Test[2]; // Test(int) Test(int)两次构造 delete p1; // ~Test() delete []p2;// ~Test() ~Test()
函数调用过程中,对象背后调用的方法太多
// 正确写法 Test GetObject(Test t) { int val = t.getData(); Test tmp(val); return tmp; } // 错误写法 Test* GetObject(Test t) { int val = t.getData(); Test tmp(val); return &tmp; } /* 因为这里的tmp是在函数里的局部对象,所以就算tmp传递一个指针,我们通过返回值知道地址后,但是这个函数运行结束后,这个内存就没了,所以这个对象的内存也就没了,那你知道了他的地址也没用了,所以不能返回局部的或者临时对象的指针或者引用 */ // 正确写法 Test* GetObject(Test t) { int val = t.getData(); static Test tmp(val); return &tmp; } /* 这样就可以,因为static是一开始就分类的内存,他一直都在 */
#include <iostream> using namespace std; Class Test { public: Test(int data = 10) :ma(data) //初始化ma的值 { cout << "Test(int)" << endl; } ~Test() { cout << "~Test()"<< endl; } Test(const Test& t) :ma(t.ma) { cout << "Test(const Test&)" << endl; } private: int ma; }; Test GetObject(Test t) { int val = t.getData(); Test tmp(val); return tmp; } int main() { Test t1; Test t2; t2 = GetObject(t1); return 0; }
这三行代码,其实调用了11个函数
三条对象优化的规则
1.函数参数传递过程中,对象优先按引用传递,不要按值传递
2.函数返回对象的过程中,应该优先返回一个临时对象,而不要返回一个定义过的对象
3.接收返回值是对象的函数调用的时候,优先按初始化的方式接受,不要按赋值的方式接收
把上面的代码优化成这样
Test GetObject(Test &t) // 按引用传递,这样这里就少了一个构造 { int val = t.getData(); //Test tmp(val); //return tmp; return Test(val); // 返回临时对象,这样少了上面两行的构造 } int main() { Test t1; Test t2 = GetObject(t1); //按初始化的方式接受 //t2 = GetObject(t1); return 0; }
这是第二条优化
这是第三条:
优化后,函数调用从11个变成了4个