函数对象,以及其构造函数等,在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个