effective c++条款17学习总结--在operator=中检查给自己赋值的情况
2011-03-25 10:21
274 查看
1. 做类似下面的事时,就会发生自己给自己赋值的情况:
class x { ... };
x a;
a = a; // a赋值给自己
另外当x& b=a; 那么b和a都是同一个对象,所以b = a;实际上也是自赋值。这是别名的一个例子:同一个对象有两个以上的名字别名可以以大量任意形式的伪装出现,所以在写函数时一定要时时考虑到它。
在赋值运算符中要特别注意可能出现别名的情况,其理由基于两点。
其中之一是效率。如果可以在赋值运算符函数体的首部检测到是给自己赋值,就可以立即返回,从而可以节省大量的工作,否则必须去实现整个赋值操作。例如,条款16指出,一个正确的派生类的赋值运算符必须调用它的每个基类的的赋值运算符,所以在派生类中省略赋值运算符函数体的操作将会避免大量对其他函数的调用。
另一个更重要的原因是保证正确性。一个赋值运算符必须首先释放掉一个对象的资源(去掉旧值),然后根据新值分配新的资源。在自己给自己赋值的情况下,释放旧的资源将是灾难性的,因为在分配新的资源时会需要旧的资源。
看看下面string对象的赋值,赋值运算符没有对给自己赋值的情况进行检查:
class string {
public:
string(const char *value); //
函数定义参见条款11
//
~string(); //
函数定义参见条款11
//
...
string& operator=(const string& rhs);
private:
char *data;
};
// 忽略了给自己赋值的情况
// 的赋值运算符
string& string::operator=(const
string& rhs)
{
delete [] data; // delete old memory
// 分配新内存,将rhs的值拷贝给它
data = new char[strlen(rhs.data) + 1];
strcpy(data, rhs.data);
return *this; // see item 15
}
看看下面这种情况将会发生什么:
string a = "hello";
a = a; // same as a.operator=(a)
赋值运算符内部,*this和rhs好象是不同的对象,但在现在这种情况下它们却恰巧
是同一个对象的不同名字。可以这样来表示这种情况:
*this data ------------> "hello/0"
/
/
rhs data -----
赋值运算符做的第一件事是用delete删除data,其结果将如下所示:
*this data ------------> ???
/
/
rhs data -----
现在,当赋值运算符对rhs.data调用strlen时,结果将无法确定。这是因为data被
删除的时候rhs.data也被删除了,data,this->data 和rhs.data 其实都是同一个
指针!从这一点看,情况只会越变越糟糕。
现在可以知道,解决问题的方案是对可能发生的自己给自己赋值的情况先进行检查
,如果有这种情况就立即返回。不幸的是,这种检查说起来容易做起来难,因为你
必须定义两个对象怎么样才算是“相同”的。
那么怎么定义两个对象实际上是不是同一个对象呢?有两种基本的方法:一种是直接比较两个对象的值,另一种是比较两个对象的内存地址。一般选择后者。
class x { ... };
x a;
a = a; // a赋值给自己
另外当x& b=a; 那么b和a都是同一个对象,所以b = a;实际上也是自赋值。这是别名的一个例子:同一个对象有两个以上的名字别名可以以大量任意形式的伪装出现,所以在写函数时一定要时时考虑到它。
在赋值运算符中要特别注意可能出现别名的情况,其理由基于两点。
其中之一是效率。如果可以在赋值运算符函数体的首部检测到是给自己赋值,就可以立即返回,从而可以节省大量的工作,否则必须去实现整个赋值操作。例如,条款16指出,一个正确的派生类的赋值运算符必须调用它的每个基类的的赋值运算符,所以在派生类中省略赋值运算符函数体的操作将会避免大量对其他函数的调用。
另一个更重要的原因是保证正确性。一个赋值运算符必须首先释放掉一个对象的资源(去掉旧值),然后根据新值分配新的资源。在自己给自己赋值的情况下,释放旧的资源将是灾难性的,因为在分配新的资源时会需要旧的资源。
看看下面string对象的赋值,赋值运算符没有对给自己赋值的情况进行检查:
class string {
public:
string(const char *value); //
函数定义参见条款11
//
~string(); //
函数定义参见条款11
//
...
string& operator=(const string& rhs);
private:
char *data;
};
// 忽略了给自己赋值的情况
// 的赋值运算符
string& string::operator=(const
string& rhs)
{
delete [] data; // delete old memory
// 分配新内存,将rhs的值拷贝给它
data = new char[strlen(rhs.data) + 1];
strcpy(data, rhs.data);
return *this; // see item 15
}
看看下面这种情况将会发生什么:
string a = "hello";
a = a; // same as a.operator=(a)
赋值运算符内部,*this和rhs好象是不同的对象,但在现在这种情况下它们却恰巧
是同一个对象的不同名字。可以这样来表示这种情况:
*this data ------------> "hello/0"
/
/
rhs data -----
赋值运算符做的第一件事是用delete删除data,其结果将如下所示:
*this data ------------> ???
/
/
rhs data -----
现在,当赋值运算符对rhs.data调用strlen时,结果将无法确定。这是因为data被
删除的时候rhs.data也被删除了,data,this->data 和rhs.data 其实都是同一个
指针!从这一点看,情况只会越变越糟糕。
现在可以知道,解决问题的方案是对可能发生的自己给自己赋值的情况先进行检查
,如果有这种情况就立即返回。不幸的是,这种检查说起来容易做起来难,因为你
必须定义两个对象怎么样才算是“相同”的。
那么怎么定义两个对象实际上是不是同一个对象呢?有两种基本的方法:一种是直接比较两个对象的值,另一种是比较两个对象的内存地址。一般选择后者。
相关文章推荐
- effective C++笔记之条款17: 在operator=中检查给自己赋值的情况
- 条款 17: 在 operator=中检查给自己赋值的情况
- 条款十七: 在operator=中检查给自己赋值的情况
- 在operator=中检查给自己赋值的情况
- effective c++(17)在operator=中检查是否“自己赋值给自己”
- 在operator=中检查给自己赋值的情况
- 条款11:记得在operator=中处理自赋值的情况。
- effective c++条款14学习总结
- effective c++条款15学习总结
- effective c++条款16学习总结
- Effective C++条款11解读: 在operator=中处理“自我赋值”------顺便给出string类的Big Three
- effective c++条款13学习总结
- Effective C++ 第二版 17)operator=检查自己 18)接口完整 19)成员和友元函数
- 【c++编程思想学习笔记】operator=为何要检查自赋值的解答
- effective c++条款13学习总结
- effective c++条款13学习总结
- 《Effect C++》学习------条款11:在 operator= 中处理“自我赋值”
- Effective C++——》条款11:在operator=中处理自我赋值
- effective c++条款10-12(operator=(重载返回类型、自我赋值和深层复制))整理
- effective c++条款11学习总结