程序说明
下面的程序都基于这样的一个test类和fun函数:
class test
{
public:
test(int n):num(n)
{
cout << "构造函数的调用" << endl;
}
test(test& p)
{
num = p.num;
cout << "拷贝的调用" << endl;
}
void show()
{
cout << "num的值为:" << num << endl;
}
~test()
{
cout << "析构函数的调用" << endl;
}
private:
int num;
};
fun函数:
void fun(test p)
{
p.show();
}
基本概念
什么是无名对象?
顾名思义,那就是:
创建时没有名字的对象。
格式:
类名(参数列表);
特点:
仅在创建语句有意义。出了创建语句就被析构。
int main()
{
test(1);
test(2);
return 0;
}
//输出结果:
//构造函数的调用
//析构函数的调用
//构造函数的调用
//析构函数的调用
可以看到,这两行语显然没有遵循 同一作用域内,先构造的对象先析构 这一准则。
当第一个无名对象创建完毕后,开始执行下一条语句之前就被析构了。
其无法调用拷贝构造函数(可能)
int main()
{
test t1(1);
test(t1);//错误
//错误详情:“test t1”: 重定义
return 0;
}
使用无名对象去初始化一个新有名对象的时候不会调用拷贝构造函数
具体情况请查看_较为特殊的情况_
无名对象可以当作左值
此种操作可以有,但是没有任何意义,因为其无法接收右值的内容。
引用可以延长无名对象的生命周期
int main()
{
test& p = test(8);
p.show();
return 0;
}
//输出结果
//构造函数的调用
//num的值为:8
//析构函数的调用
可以看到,无名对象被引用后,其并未立即析构,且可以调用成员函数,说明,一个无名对象被引用后,就相当于有了名字,其生命周期就会被延长
作用:
无名对象的目的是为了给别的有名对象或引用服务的。
使用情况
在函数返回一个对象时
当函数返回一个对象的时候, 会在在内核中会生成一个无名对象
但是这种情况下的无名,是指程序员不可查看的无名,实际上是有名字的
给有名对象提供资源
int main()
{
test t1(3);
t1 = test(5);
; t1.show();
return 0;
}
//输出结果
//构造函数的调用
//构造函数的调用
//析构函数的调用
//num的值为:5
//析构函数的调用
将无名对象赋值给有名对象。
较为特殊的情况
使用无名对象初始化一个新对象
int main()
{
test tmp1 = test(5);
return 0;
}
//运行结果:
//构造函数的调用
//析构函数的调用
int main()
{
test t1(test(1));
return 0;
}
//运行结果:
//构造函数的调用
//析构函数的调用
按照以前的理解,首先应该会创建一个匿名对象,然后再调用拷贝构造函数创建tmp1,所以此过程应该有构造函数的调用。
但是可以看到,运行结果可能和我们想像的不太一样。并没有构造函数的调用。
原因:
首先,我们无法对一个无名对象通过对无名对象进行拷贝来创建新对象,所以上述过程中的第二步调用拷贝构造创建tmp1就不能够实现了
其次,上述过程中临时对象的生成是没有任何意义的,所以过程会被编译器优化为test 1(1);
所以,结果中的一次构造函数的调用是创建tmp1的构造函数调用。
无名对象当作实参
int main()
{
fun(test(1));
return 0;
}
//输出结果为:
//构造函数的调用
//num的值为:1
//析构函数的调用
int main()
{
test tmp1(3);
fun(tmp1);
return 0;
}
//输出结果为:
//构造函数的调用
//拷贝的调用
//num的值为:3
//析构函数的调用
//析构函数的调用
可以看到,当fun传入有名对象的时候,会调用两次构造函数,分别是tmp1对象创建时调用构造函数和创建p对象时调用的拷贝构造函数。
但是当传入参数时无名对象的时候,构造函数只被调用一次,说明整个过程中,那么是怎么回事呢?
原因:
首先,我们无法对一个无名对象通过对无名对象进行拷贝来创建新对象,所以上述过程中的第二步调用拷贝构造创建p就不能够实现了
其次,上述过程中临时对象的生成是没有任何意义的,所以过程会被编译器优化,直接以无名参数的参数列表去创建形参p。所以结果中的构造函数是形参p构建调用的构造函数。
函数返回匿无名对象
test fun1()
{
return test(4);
}
int main()
{
test tmp = fun1();
tmp.show();
}
//输出结果
//构造函数的调用
//tmp.show();
//析构函数的调用
可以看到生个过程中只有调用过一次构造函数。此过程中并不像返回普通局部对象或便来给你不同,并不会产生内核空间中的临时对象,而是直接构造tmp.