高效率的C++函数返回值

来源:互联网 发布:bing软件 编辑:程序博客网 时间:2024/05/11 12:52

1.有时候返回引用可以提高效率,有些情况不能返回引用

类的赋值运算符函数中,返回对象的引用可以显著提高效率。

 

[cpp:nogutter] view plaincopy
  1. class String  
  2. {  
  3. public:   
  4.     String & operate=(const String &other);   
  5.     // 相加函数,如果没有friend修饰则只许有一个右侧参数  
  6.     friend  String   operate+(const String &s1, const String &s2);   
  7. private:  
  8.     char *m_data;   
  9. }  
  10.   
  11. String & String::operate=(const String &other)  
  12. {  
  13.     if (this == &other)  
  14.         return *this;  
  15.     delete m_data;  
  16.     m_data = new char[strlen(other.data)+1];  
  17.     strcpy(m_data, other.data);  
  18.     return *this;   // 返回的是 *this的引用,无需拷贝过程  
  19. }  

上面如果用“值传递”的方式,虽然功能仍然正确,但由于return语句要把 *this拷贝到保存返回值的外部存储单元之中,增加了不必要的开销,降低了赋值函数的效率。

String的相加函数operate + 的实现如下:

 

[cpp:nogutter] view plaincopy
  1. String  operate+(const String &s1, const String &s2)    
  2. {  
  3.     String temp;  
  4.     delete temp.data;   // temp.data是仅含‘/0’的字符串  
  5.     temp.data = new char[strlen(s1.data) + strlen(s2.data) +1];  
  6.     strcpy(temp.data, s1.data);  
  7.     strcat(temp.data, s2.data);  
  8.     return temp;  
  9. }  

对于相加函数,应当用“值传递”的方式返回String对象。如果改用“引用传递”,那么函数返回值是一个指向局部对象temp的“引用”。由于temp在函数结束时被自动销毁,将导致返回的“引用”无效。

设想一下,相加运算符重载函数应该是参数列表中的两个对象相加,必须产生一个新的对象并返回它。这区别于+=运算符,+=运算符是将右边的对象累加到左边的对象上,因此+=的实现应当返回运算符左边对象的引用。

 

2. 返回静态局部变量的引用往往会带来问题
上面已经说了operator+函数中不能返回临时对象的引用,那么返回静态局部变量的引用呢?静态局部变量的生命不会随着函数消失而结束,返回其引用似乎可行,但这样做往往会给程序带来问题。

 

[cpp:nogutter] view plaincopy
  1. const Rational& operator*(const Rational& lhs, const Rational& rhs)  
  2. {  
  3.     static Rational result;             // static object to which a reference will be returned  
  4.     result = ... ;                      // multiply lhs by rhs and put the product inside result  
  5.     return result;  
  6. }  

 下面if恒为真,静态局部变量对全局可见,每次operator*返回的是它的引用,相当于是返回它的“新值”。

[cpp:nogutter] view plaincopy
  1. bool operator==(const Rational& lhs, const Rational& rhs); // an operator== for Rationals  
  2. Rational a, b, c, d;  
  3. ...  
  4. if ((a * b) == (c * d))    
  5. {  
  6.     do whatever's appropriate when the products are equal;  
  7. }  
  8. else   
  9. {  
  10.     do whatever's appropriate when they're not;  
  11. }  

 

3. 返回临时对象,不要定义局部对象返回
String temp(s1 + s2);
return temp;
上述代码将发生三件事。首先,temp对象被创建,同时完成初始化;然后拷贝构造函数把temp拷贝到保存返回值的外部存储单元中;最后,temp在函数结束时被销毁(调用析构函数)。然而,创建一个临时对象并返回它:return temp(s1 + s2);,编译器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了效率。
由于内部数据类型如int,float,double的变量不存在构造函数与析构函数,虽然该“临时变量的语法”不会提高多少效率,但是程序更加简洁易读。

 

4. 注意不要返回栈指针和常量字符串
不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡。

[cpp:nogutter] view plaincopy
  1. char *GetString(void)  
  2. {  
  3.     char p[] = "hello world";  
  4.     return p;   // 编译器将提出警告  
  5. }  
  6.   
  7. void Test4(void)  
  8. {  
  9.     char *str = NULL;  
  10.     str = GetString();  // str 的内容是垃圾  
  11.     cout<< str << endl;  
  12. }  

函数Test5运行虽然不会出错,但是函数GetString2的设计概念却是错误的。因为GetString2内的“hello world”是常量字符串,位于静态存储区,它在程序生命期内恒定不变。无论什么时候调用GetString2,它返回的始终是同一个“只读”的内存块。

[cpp:nogutter] view plaincopy
  1. char *GetString2(void)  
  2. {  
  3.     char *p = "hello world";  
  4.     return p;  
  5. }  
  6.   
  7. void Test5(void)  
  8. {  
  9.     char *str = NULL;  
  10.     str = GetString2();  
  11.     cout<< str << endl;  
  12. }  

 

5. 谨慎返回对象内部成员对象的引用
有时候希望使得类对象占用空间较小,通常可以使用一个指向对象数据的指针作为该类的数据成员,如Rectangle类的成员指针pData。类的成员函数upperLeft和lowerRight仅仅是想获取类的数据内容而不想修改其内容,因此函数结尾使用了const表示类的数据成员不可更改。然而却不能达到设计的目的,虽然成员是私有,然而却通过公有函数返回了可以修改它的引用。如可以这样修改它:Rectangle r(&rd); r.upperLeft().setX(5);

[cpp:nogutter] view plaincopy
  1. class Point  
  2. {  
  3. public:  
  4.     Point(float p1, float p2){ x = p1; y = p2; }  
  5.     void setX(float f){ x = f; }  
  6.     void setY(float f){ y = f; }  
  7. private:  
  8.     float x, y;  
  9. };  
  10.   
  11. struct RectData  
  12. {  
  13.     RectData(Point p1, Point p2) : ulhc(p1), lrhc(p2){}  
  14.     Point ulhc, lrhc;  
  15. };  
  16.   
  17. class Rectangle   
  18. {  
  19. public:  
  20.     Rectangle(RectData *p) { pData = p; }  
  21.     Point& upperLeft() const { return pData->ulhc; }  
  22.     Point& lowerRight() const { return pData->lrhc; }  
  23. private:  
  24.     RectData *pData;  
  25. };  

因此需要将两函数改为,这样想试图修改点的数据内容时,编译器会报错。

[cpp:nogutter] view plaincopy
  1. const Point& upperLeft() const { return pData->ulhc; }  
  2. const Point& lowerRight() const { return pData->lrhc; }