pass-by-value与pass-by-reference比较起来效率低很多,优先选择后者。但要注意有时候返回引用或指针会带来问题。
class Rational
{
public:
friend const Rational& operator*(const Rational& lhs,const Rational& rhs)
{
Rational res(lhs.n*rhs.n,lhs.d*rhs.d);
return res;
}
Rational(int l,int r){}
private:
int n;
int d;
};
*操作符重载函数有没有问题?
1.效率上来看,这里需要调用一次构造函数生成一个对象,然后将其返回,res作为局部对象在销毁时会调用一次析构函数。
2.res作为局部对象会被销毁,内存空间会被清理,如此,返回的是一个已经不存在的对象的引用。
这是由于栈对象的特点决定的,引用的前提是对象必须已经存在!那么返回一个堆对象引用或指针可行不?
class Rational
{
public:
friend const Rational& operator*(const Rational& lhs,const Rational& rhs)
{
Rational* res = new Rational (lhs.n*rhs.n,lhs.d*rhs.d);
return *res;
}
Rational(int l,int r){}
private:
int n;
int d;
};
说明1:从效率上来看,调用一次构造函数进行初始化。
说明2:delete 动作交给 了客户端
说明3:Rational w,x,y,z;w =x*y*z;客户端这样使用时,会有什么问题?
调用了两次*动作,就是new 两次了,当然需要delete 两次,但无法获取delete 所需要的指针。
堆栈空间上分配的对象都不能完成返回值是对象的需求,那么static 对象怎么样?
class Rational
{
public:
friend const Rational& operator*(const Rational& lhs,const Rational& rhs)
{
static Rational res ;
res.n = lhs.n*rhs.n;
res.d = lhs.d*rhs.d;
return res;
}
friend bool operator == (const Rational& lhs,const Rational& rhs){}
Rational(int l,int r){}
private:
int n;
int d;
};
说明1:所有static 对象都有的一个安全隐患,多线程应用时
说明2:考虑客户端这样的使用情形:
Rational a,b,c,d;
if(a*b == c*d)
{}
else
{}
<==>
if(operator==(operator*(a,b)),(operator*(c,d)))
**operator*返回的是一个static对象的引用,不同的对象运算得到的static对象虽然不同,但static对象只有一份,最终比较的时候由于指向的是同一个static 对象,所以永远为true。**
那么使用静态数组对象怎么样?效率大大降低了。首先数值需要指定大小,就算使用vector 替代之,还有另外一个问题,对象之间的赋值,拷贝构造函数和析构函数的调用问题。
最好的办法是:pass-by-value
class Rational
{
public:
/*friend const Rational operator*(const Rational& lhs, const Rational& rhs)
{
Rational res;
res.n = lhs.n*rhs.n;
res.d = lhs.d*rhs.d;
return res;//拷贝构造匿名对象,至于匿名对象的析构则要看其如何被接收
}//析构res 局部对象
*/
friend const Rational operator*(const Rational& lhs, const Rational& rhs)
{
return Rational(lhs.n*rhs.n, lhs.d*rhs.d);//一次有参构造函数生成匿名对象,效率更高
}
Rational(int l,int r){}
private:
int n;
int d;
};
说明:为了满足返回值是对象的需求,拷贝构造函数被调用,生成一个匿名对象,然后将其返回,而res会被销毁。但这样仍然没有解决多次调用构造函数和析构函数带来的效率问题,庆幸的是C++ 编译器可以在特殊情况下进行优化。
Rational a, b, c;
c = a*b;
Rational d = a*b;
说明:对象c 和对象d 的区别在于
Rational d = a*b;
匿名对象初始化对象d,此时匿名对象和d合二为一,d对象生命周期结束时调用析构函数释放匿名对象。此过程没有调用拷贝构造函数或=运算符重载函数,这是编译器的优化,在于过程是初始化过程,效率更高。
Rational a, b, c;//初始化
c = a*b;//赋值
过程是赋值过程,非初始化过程。所以需要调用=操作符重载函数,效率低。