19 了解临时对象的来源

C++真正的所谓的临时对象是不可见的——不会再你的源码中出。只要你产生一个non-heap object而没有为它命名,便会产生一个临时对象。

匿名对象通常发生于两种情况:一是隐式类型转换被实施起来以求函数调用成功;二是当函数返回对象的时候。

首先靠考虑为了让函数调用成功”而产生的临时对象。此乃发生于“传递某对象给一个函数,而其类型和它即将绑定上去的参数类型不同”的时候。

如下代码,考虑一个函数,用来计算字符串中的某字符出现次数:

//返回ch在str中出现的次数
size_t countChar(const string& str,char ch);

char buffer[MAX_STRING_LEN];
char c;
//读入一个char和一个string,利用setw避免
//在读入string的时候产生缓冲区溢出的情况。
cin >> c >> setw(MAX_STRING_LEN) >> buffer;

cout << "There are" << countChar(buffer,c)
	 << "occurrences of the character" << c_str
	 <<"in" << buffer <<endl;

在调用countChar的时候,第一个自变量是一个char数组,但是函数参数确实const string& 、当“类型不吻合”的状态消除,此函数才调用成功;编译器便会产生一个类型string的临时对象,该对象的初始化方式:以buffer作为参数,调用string的构造函数。所以countChar的str参数会绑定在string临时对象上,当countChar返回,此临时对象自动销毁。

只有当对象by value(传值)方式传递,或是当对象被传递给一个reference-to-const参数时,这些转换才会发生。如果对象被传递给一个reference-to-non-const参数,并不会发生此类转换。

 考虑如下一个函数:

//把str中所有字符改成大写
void uppercasify(string& str);

在字符计算的例子里,能够成功传递char数组到countChar中,但是在这里试图用char数组调用uppercasify函数,则不会成功:

char subtleBookPlug[] = "Effective C++";
uppercasify(subtleBookPlug); //错误! 

引用别人表述:

c++编译器的一个关于语义的限制。如一个参数是以非const引用传入,c++编译器就有理由认为程序员会在函数中修改这个值,并且这个被修改的引用在函数返回后要发挥作用。如果你一个临时变量当作非const引用参数传进来,由于临时变量的特殊性,程序员并不能操作临时变量,而且临时变量随时可能被释放掉,所以,一般说来,修改一个临时变量是毫无意义的,据此,c++编译器加入了临时变量不能作为非const引用的这个语义限制,意在限制这个非常规用法的潜在错误。

说直白一点,如果你把临时变量作为非const引用参数传递,一方面,在函数申明中,使用非常量型的引用告诉编译器你需要得到函数对某个对象的修改结果,可是你自己又不给变量起名字,直接丢弃了函数的修改结果,编译器只能说:“大哥,你这是干啥呢,告诉我把结果给你,等我把结果给你了,你又直接给扔了,你这不是在玩我呢吗?”所以编译器一怒之下就不让过了。这下大家明白了吧?

建立临时对象第二种环境返回对象时,例如operator+必须返回一个对象,以表示它的两个操作数的和。例如:

const Number operator+(const Number& lhs,const Number& rhs);

这个函数返回式临时的,因为它没有被命名。它只是函数的返回值。你必须为每次的调用operator+构造和释放这个对象而付出代价。

综上所述:临时对象是有开销的,所以你应该尽量去避免使用它们,然而更重要的是训练自己寻找可能建立临时对象的地方。在任何地方只要见到常量引用(reference-to-const)参数,就存在建立临时对象而绑定参数的可能。在任何时候只要见到函数返回对象,就会有一个临时对象被建立(然后被释放)。学会寻找这种对象,你就能显著的增强透过编译器表面动作而看到其背后的开销。

猜你喜欢

转载自blog.csdn.net/weixin_28712713/article/details/81231498
19