2011年(38)
分类: LINUX
2011-05-17 00:31:44
关于nonmodifying运算符重载的常见迷思
详细:
1.概念:所谓nonmodifying运算符就是不改变操作数的值,并且计算结果是一个与操作同类型的对象的运算符。比如数学运算符:+,-,*,/,%.而关系运算符(bool类型)和赋值运算符(改变左边的操作数)则不是nonmodifying运算符。
2.与nonmodifying运算符有关的主要问题是返回值。为了提高效率节省时间,决定用引用方式来返回运算结果,而不是返回值方式(隐式调用拷贝构造函数)。返回引用相关的临时变量时,会出现内存泄漏问题。(用引用方式返回局部变量是错误的----当函数退出时,与这个引用相关的内存也就退回了栈。从而使函数调用者得到一个指向无效的、没有分配到内存的对象)
例子:
#include
#include
using namespace std;
class Point
{
private:
int x;
int y;
char *color;
public:
Point(int =0 , int =0 , char * ="white");
~Point();
Point(const Point&);
void print();
const Point& operator=(const Point&);
const Point& operator+(const Point&);
//正确的是: const Point operator+(const Point&);
};
Point::Point(int new_x , int new_y , char *col)
{
x=new_x;
y=new_y;
color=new char[strlen(col)+1];
strcpy(color,col);
}
Point::~Point()
{
delete color;
}
Point::Point(const Point& rhs)
{
x=rhs.x;
y=rhs.y;
color=new char[strlen(rhs.color)+1];
strcpy(color,rhs.color);
}
const Point& Point::operator=(const Point& rhs)
{
if(&rhs==this)
return (*this);
x=rhs.x;
y=rhs.y;
delete color;
color=new char[strlen(rhs.color)+1];
strcpy(color,rhs.color);
return (*this);
}
//此函数返回一个指向临时Point对象的隐指针(一个引用),这个指针在函数退出时就被释放啦
const Point& Point::operator+(const Point& rhs)
{
Point temp;
temp.x=x+rhs.x;
temp.y=y+rhs.y;
delete temp.color;
temp.color=new char[strlen(color)+strlen(rhs.color)+1];
sprintf(temp.color,"%s%s",color,rhs.color);
return (temp);
//waring:returning address of local variable or temporary
}
void Point::print()
{
cout<<"I live at ( "
<
int main()
{
Point p1(10,10,"Blue");
Point p2(20,60,"Green");
Point p3=p1+p2;
/*拷贝构造函数的参数是一个已经被释放的Point对象,因为运算符+重载函数返回的是指向自动变量temp的隐指针。这个变量在退出运算符重载函数时就调用了它的析构函数*/
p3.print();
return 0;
}
*------------------------------------------------------------------------
*解决问题的一种方法是:将临时的Point对象作为类的内部静态存储对象。内部 *静态对象不会在进入/退出函数的时候被创建/释放,它只是进入/退出作用域。*但是对于嵌套调用(比如:x+y+z)就会出现问题啦
*----------------------------------------------------------------------*
//修改代码如下:
const Point& Point::operator+(const Point& rhs)
{
static Point temp;
//注意内部静态存储对象temp的使用。每一次调用这个函数时读和写的对象时
//同一个。
temp.x=x+rhs.x;
temp.y=y+rhs.y;
delete temp.color;
temp.color=new char[strlen(color)+strlen(rhs.color)+1];
sprintf(temp.color,"%s%s",color,rhs.color);
return (temp);
}
*-------------------------------------------------------------------------
*还有一种思路是利用动态分配来解决,但是由于动态分配的临时变量的析构函
*数并没有自动的被调用,即它们没有被收回。此外得不到对象的地址,函数的
*调用者也不能显示的调用析构函数。
*-----------------------------------------------------------------------*
//代码如下:
const Point& Point::operator+(const Point& rhs)
{
Point *temp=new Point;
delete temp->color;
temp.x=x+rhs.x;
temp.y=y+rhs.y;
delete temp.color;
temp.color=new char[strlen(color)+strlen(rhs.color)+1];
sprintf(temp.color,"%s%s",color,rhs.color);
return (*temp);
}
*-------------------------------------------------------------------------
综上所述:
nonmodifying运算符返回的必须是一个对象,而不是一个对象的引用。
const Point Point::operator+(const Point& rhs)
{
Point temp;
temp.x=x+rhs.x;
temp.y=y+rhs.y;
delete temp.color;
temp.color=new char[strlen(color)+strlen(rhs.color)+1];
sprintf(temp.color,"%s%s",color,rhs.color);
return (temp);
}