c/c++:efficient c++,临时对象
说个题外话
#include <iostream> using namespace std; class A { public: char a; char b; char c; public: A(char aa, char bb=97, char cc=97):a(aa),b(bb),c(cc){} }; int main() { A e=98; cout<<e.a<<e.b<<e.c<<endl; return 0; }
对于 下面的 A e= 98; 是能够匹配到A的构造函数的,aa 需不需要默认值都可以,这样的声明会将 98 赋予第一个 参数,即aa 。
主要内容:
class Rational { friend Rational operator +(const Rational &,const Rational &); public: Rational(int a=0,int b=1):m(a),n(b){} private: int m; int n; };
对于对象定义:
Rational r1(100); Rational r2 = Rational(100); Rational r3= 100;
只有第一个r1 是不会创建临时变量的,另外两个都会。不过编译器优化了,所以3者效率一样。
对于类型不匹配:
Rational r; r=100;
这样第二句会寻找Ratiaonal 的构造函数(如我上面写的那个题外话),然后再调用拷贝函数实现。
想禁止这用转换,可以再构造函数(对于其他什么函数都适用)前面加 explicit
class Rational { public: explicit Rational(int a=0, int b=1):m(a),n(b){} };
有时候调用函数时会产生临时变量的,如
void g(const string &s){} g("message");
下面的调用函数中传递的是指针。
再按例子 Complex类
complex a,b; for(int i=0;i<100;i++) { a= i*b +1.0; } complex a,b; complex one(1,0); for(int i=0;i<100;i++) { a= i*b +one; }
上面部分中的1.0 需要不断的创建于销毁。
按值传递:
一般的调用:
T t;
g(t);
编译器将需要创建T 类型的临时对象,并且用t 作为输入参数调用拷贝构造函数创建这个变量,然后以这个变量作为实参传递给个g(),然后返回时调用析构函数释放临时变量。
按值返回:
调用函数会创建变量保存返回值,如果有赋值语句就调用赋值函数,最后是释放临时变量。
通过上一章可以消除在被调用函数中的临时变量 (RVO)。
至于消除当前函数的临时变量。就要考虑。
对于语句 s3=s1 +s2. 源函数会产生一个临时变量保存 s1+s2 的值。为什么会产生呢?
这是两个操作 ,+ = ,对于s1+s2 部分没有权利修改s3 的值。(这是 = 的是功能)。
如果s3 有旧值时,不能将s3 作为这一临时变量,但如果s3 是新声明的,便能直接作为该变量,这样就省了一个临时变量。
写法:
//有临时变量 string s1= "Hello "; string s2= "World "; string s3; s3=s1+s2; //无临时变量 string s1= "Hello "; string s2= "World "; string s3=s1+s2;
至于在s3 有旧值的情况下,可以这样来避免创建临时变量。
//有临时变量 string s1,s2,s3,s4; s1=s2+s3+s4; //无临时变量 s1=s2; s1+=s3; s1+=s4;
要点:
1.临时对象会以构造函数和析构函数的形式降低一半的性能。
2.将构造函数声明为 explicit,可以阻止编译器幕后使用类型转换。
3.编译器常常创建临时对象来解决类型不匹配问题。通过重载可以避免这种情况。
4.如果肯尼个,尽量避免使用对象拷贝(应该指函数调用的时候),按引用传递和返回对象。
5.在<op>可能是“+-*/”的地方,使用 <op>=运算符可以消除临时对象。