【转】高效率的C++函数返回值
时间:2010-10-06 来源:陈峰
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