Effective C++笔记2
来源:互联网 发布:淘宝大v达人申请 编辑:程序博客网 时间:2024/05/17 23:50
【条款11】为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
默认的拷贝构造函数和赋值运算符,是浅拷贝的。如果有指针成员变量,默认只会拷贝指针值,而不会为指针分配新的内存。这会带来两个问题:
1.内存泄露。拷贝指针时,并没有释放原来指向的内存,导致这部分内存泄露;
2.两个指针指向同一块内存。如果一个对象的生命周期结束,释放了这部分内存,另一个指针就会指向已经释放的内存。这个对象生命周期也结束时,会delete已经delete了的指针。
例:
string a("hello");
{
string b("world");
b = a; // "world"所占内存泄露了
}
string c = a; // b生命周期结束,释放了"hello"内存,a和c的值不确定
【条款12】尽量使用初始化,而不要在构造函数里赋值
在使用成员之前,要确保它已经被赋值,有两种方法:
1.初始化。方法是用初始化列表;
2.在构造函数中赋值。
如果使用初始化,会调用成员变量的构造函数;如果在构造函数中赋值,会先调用成员变量的默认构造函数进行初始化,然后调用operator=运算符进行赋值。
很明显,使用初始化效率比较高。
另外,如果成员变量是const或引用,则不得不用初始化列表,否则会编译错误。
【条款13】初始化列表中成员列出的顺序和它们在类中声明的顺序相同
成员按照声明的顺序初始化,与初始化列表中的顺序无关。否则,类需要记住初始化列表顺序,然后按照反顺序调用析构函数,太麻烦了。
如果初始化列表中的顺序和声明的顺序不一致,可能会带来问题,例如用一个未初始化的变量初始化另一个变量。
基类初始化应该放在初始化列表的最前面。如果有多个基类,初始化顺序应和继承顺序一致。
【条款14】确定基类有虚析构函数
B继承了A。A类型指针p指向B对象。delete p时,如果A的析构函数不是虚函数,就没有多态,只调用了A的析构,而没有调用B的析构,这可能会引起内存泄漏。
【条款15】让operator=返回*this的引用
string& operator=(const string& rhs); // 将一个string赋给一个string
问题1:为什么参数要是const的?
x = "hello";
编译器会产生一个临时string变量,并且是const的。如果参数不是const的,x = "hello"这种用法就不对了。
问题2:为什么要返回string&,而不是void?
int w, x, y, z;
w = x = y = z = 0;
固定类型可以这样赋值,我们自定义的类应该跟其保持一致。如果返回void,就不能连续赋值了。
问题3:为什么不能返回参数rhs?
int i1, i2, i3;
...
(i1 = i2) = i3;
对于固定类型,这样是合法的,i2赋给i1,然后i3赋给i1。
如果返回参数rhs,它是const的,就不能这样用了。
综上,operator=应当返回*this的引用。
string& string::operator=(const string& rhs)
{
...
return *this;
}
【条款16】在operator=中对所有数据成员赋值
在operator=中,应该对所有数据成员赋值。
比较容易出错的是继承的时候,忘了调用基类的operator=。这样基类就不会被赋值。
基类operator=的调用方法:
derived& derived::operator=(const derived& rhs)
{
if (this == &rhs) return *this;
base::operator=(rhs); // 调用this->base::operator=
y = rhs.y;
return *this;
}
或者:
derived& derived::operator=(const derived& rhs)
{
if (this == &rhs) return *this;
static_cast<base&>(*this) = rhs; // 对*this的base部分
// 调用operator=
y = rhs.y;
return *this;
}
注意,*this转成base&,而不是base,否则会调用copy构造函数生成新的base对象,并对这个新对象赋值。
拷贝构造函数类似于operator=,一定要记得调用基类的拷贝构造函数。
class derived: public base {
public:
derived(const derived& rhs): base(rhs), y(rhs.y) {}
...
};
【条款17】在operator=中检查给自己赋值的情况
检查给自己赋值的情况,基于两个原因:
1.效率。如果是给自己赋值,没必要真的去赋值,这样可以节省赋值动作及可能的函数调用(base部分operator=)。
2.保证正确性。如果有指针成员,在赋值前要先释放内存,然后分配新的内存。给自己赋值的情况下,释放内存后,没有拷贝需要的源数据了,这是灾难性的。
检查方法:
c& c::operator=(const c& rhs)
{
// 检查对自己赋值的情况
if (*this == rhs) // 假设operator=存在
return *this;
...
}
给自己赋值,看起来可能性不大,a=a,显得无聊吗。
但是不要忘了别名的存在。a=b,b可能是a的一个别名。
所以,任何函数,只要有别名存在,都要考虑两个变量是同一个变量的情况。
【条款18】争取使类的接口完整并且最小
- 《Effective C++》 笔记
- 《Effective C++》阅读笔记
- Effective C++--笔记
- <Effective C++: 资源管理> 笔记
- <<Effective C++>>笔记1
- <<Effective C++>>笔记3
- <<Effective C++>>笔记4
- <<Effective C++>>笔记5
- 《Effective C++》学习笔记
- 《Effective C++》阅读笔记
- 《Effective C++》学习笔记
- 《Effective C++》 笔记
- 《Effective C++》笔记
- Effective C++_Item3笔记
- Effective C++_Item4笔记
- Effective C++_Item5笔记
- Effective C++_Item6笔记
- Effective C++_Item7笔记
- 转载:OS 面试相关问题
- Weblogic8.1连接SQL Server 2005(转)
- PHP数据库链接代码
- 一个简单的 Struts 例子
- 初来宝地
- Effective C++笔记2
- JS判断输入框不为空
- 不明真相的人
- Microsoft SQL Server 2005 JDBC Driver 1.1
- 转载:Bash的陷阱
- Effective C++笔记3
- 传智播客JAVA培训OA项目 审批流转模块二
- 如何将自定义的structure转换为byte数组(VB.Net)
- CListCtrl控件使用方法总结