Effective C++——必须返回对象时,别妄想返回其引用或指针

来源:互联网 发布:联通云计算公司工资 编辑:程序博客网 时间:2024/05/17 17:16

pass-by-valuepass-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;//赋值
过程是赋值过程,非初始化过程。所以需要调用=操作符重载函数,效率低。

总结:操作符重载函数采用有参数构造函数生成匿名对象,客户端采用初始化方式接收,直接将匿名对象提升为所需对象。

0 0
原创粉丝点击