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

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

 view plaincopy to clipboardprint?
class String  
{  
public:   
    String & operate=(const String &other);   
    // 相加函数,如果没有friend修饰则只许有一个右侧参数  
    friend  String   operate+(const String &s1, const String &s2);   
private:  
    char *m_data;   
}  
 
String & String::operate=(const String &other)  
{  
    if (this == &other)  
        return *this;  
    delete m_data;  
    m_data = new char[strlen(other.data)+1];  
    strcpy(m_data, other.data);  
    return *this;   // 返回的是 *this的引用,无需拷贝过程  

class String
{
public:
 String & operate=(const String &other); 
 // 相加函数,如果没有friend修饰则只许有一个右侧参数
 friend String   operate+(const String &s1, const String &s2);
private:
 char *m_data;
}

String & String::operate=(const String &other)
{
 if (this == &other)
  return *this;
 delete m_data;
 m_data = new char[strlen(other.data)+1];
 strcpy(m_data, other.data);
 return *this; // 返回的是 *this的引用,无需拷贝过程
}

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

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

 view plaincopy to clipboardprint?
String  operate+(const String &s1, const String &s2)    
{  
    String temp;  
    delete temp.data;   // temp.data是仅含‘\0’的字符串  
    temp.data = new char[strlen(s1.data) + strlen(s2.data) +1];  
    strcpy(temp.data, s1.data);  
    strcat(temp.data, s2.data);  
    return temp;  

String  operate+(const String &s1, const String &s2) 
{
 String temp;
 delete temp.data; // temp.data是仅含‘\0’的字符串
 temp.data = new char[strlen(s1.data) + strlen(s2.data) +1];
 strcpy(temp.data, s1.data);
 strcat(temp.data, s2.data);
 return temp;
}

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

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

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

 view plaincopy to clipboardprint?
const Rational& operator*(const Rational& lhs, const Rational& rhs)  
{  
    static Rational result;             // static object to which a reference will be returned  
    result = ... ;                      // multiply lhs by rhs and put the product inside result  
    return result;  

const Rational& operator*(const Rational& lhs, const Rational& rhs)
{
 static Rational result;             // static object to which a reference will be returned
 result = ... ;                      // multiply lhs by rhs and put the product inside result
 return result;
}

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

view plaincopy to clipboardprint?
bool operator==(const Rational& lhs, const Rational& rhs); // an operator== for Rationals  
Rational a, b, c, d;  
...  
if ((a * b) == (c * d))    
{  
    do whatever's appropriate when the products are equal;  
}  
else   
{  
    do whatever's appropriate when they're not;  

bool operator==(const Rational& lhs, const Rational& rhs); // an operator== for Rationals
Rational a, b, c, d;
...
if ((a * b) == (c * d)) 
{
 do whatever's appropriate when the products are equal;
}
else
{
 do whatever's appropriate when they're not;
}
 

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

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

view plaincopy to clipboardprint?
char *GetString(void)  
{  
    char p[] = "hello world";  
    return p;   // 编译器将提出警告  
}  
 
void Test4(void)  
{  
    char *str = NULL;  
    str = GetString();  // str 的内容是垃圾  
    cout<< str << endl;  

char *GetString(void)
{
 char p[] = "hello world";
 return p; // 编译器将提出警告
}

void Test4(void)
{
 char *str = NULL;
 str = GetString(); // str 的内容是垃圾
 cout<< str << endl;
}

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

view plaincopy to clipboardprint?
char *GetString2(void)  
{  
    char *p = "hello world";  
    return p;  
}  
 
void Test5(void)  
{  
    char *str = NULL;  
    str = GetString2();  
    cout<< str << endl;  

char *GetString2(void)
{
 char *p = "hello world";
 return p;
}

void Test5(void)
{
 char *str = NULL;
 str = GetString2();
 cout<< str << endl;
}
 

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

view plaincopy to clipboardprint?
class Point  
{  
public:  
    Point(float p1, float p2){ x = p1; y = p2; }  
    void setX(float f){ x = f; }  
    void setY(float f){ y = f; }  
private:  
    float x, y;  
};  
 
struct RectData  
{  
    RectData(Point p1, Point p2) : ulhc(p1), lrhc(p2){}  
    Point ulhc, lrhc;  
};  
 
class Rectangle   
{  
public:  
    Rectangle(RectData *p) { pData = p; }  
    Point& upperLeft() const { return pData->ulhc; }  
    Point& lowerRight() const { return pData->lrhc; }  
private:  
    RectData *pData;  
}; 
class Point
{
public:
 Point(float p1, float p2){ x = p1; y = p2; }
 void setX(float f){ x = f; }
 void setY(float f){ y = f; }
private:
 float x, y;
};

struct RectData
{
 RectData(Point p1, Point p2) : ulhc(p1), lrhc(p2){}
 Point ulhc, lrhc;
};

class Rectangle
{
public:
 Rectangle(RectData *p) { pData = p; }
 Point& upperLeft() const { return pData->ulhc; }
 Point& lowerRight() const { return pData->lrhc; }
private:
 RectData *pData;
};

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

view plaincopy to clipboardprint?
const Point& upperLeft() const { return pData->ulhc; }  
const Point& lowerRight() const { return pData->lrhc; } 
const Point& upperLeft() const { return pData->ulhc; }
const Point& lowerRight() const { return pData->lrhc; }

参考文献:

Effective C++

高质量 C/C++ 编程指南

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/Breathomn/archive/2009/10/11/4654830.aspx

posted on 2010-10-06 15:50  陈峰  阅读(1066)  评论(0编辑  收藏  举报