Effective C++ 部分读书笔记
2016-04-12 16:50
197 查看
界面类 和 数据类 分开
******************************************************************************************************************
******************************************************************************************************************
More Effective C++ 笔记
******************************************************************************************************************
******************************************************************************************************************
条款1.限制某个 class 所能产生的对象数量
1)类中的静态成员总是被构造,即使不使用,而且你无法确定它什么时候初始化;
2)非成员内联函数在链接的时候在目标文件中会产生多个副本,可能造成程序的静态对象拷贝超过一个。
3)限制对象个数:建立一个基类,构造函数和复制构造函数中计数加一,若超过最大值则抛出异常;析构函数中计数减一。
4)允许一个或零个对象,阻止某个类的对象被创建的最简单方法就是把这个类的构造函数声明为私有(private)。
************************************************************************
条款2.限制某个 class 所能产生的对象数量了解 virtual functions、multiple inheritance、virtual base classes、runtime type identification 的成本
1)不要将虚函数声明为 inline ,因为虚函数是运行时绑定的,而 inline 是编译时展开的,即使你对虚函数使用 inline ,编译器也通常会忽略。
条款3.考虑使用其它程序库
选用不同的库可能会大幅改善程序性能,比如说 iostream 和 stdio 库.
******************************************************************************************************************
------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------
Effective C++ 笔记
------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------
******************************************************************************************************************
条款1.尽量用:const和inline 不用:#define
const char * const PRESCRIPTION_STATE_ = "正在配置";
static const int LIST_NUM = 5; //类中声明
char *szPatientArray[LIST_NUM]; //使用
const int 类名::LIST_NUM; //定义
enum{LIST_NUM = 5}
char *szPatientArray[LIST_NUM];
template<class T>
inline const T& Max(const T&a, const T&b)
{
return a>b ? a:b;
}
******************************************************************************************************************
条款2.尽量用#include<iostream> 不用:#include<stdio.h>
论 cin/cout 和scanf/print的优缺点
#include <iostream>得到的是置于名字空间std下的iostream库的元素
#include <iostream.h>得到的是置于全局空间的同样的元素,可能产生冲突
******************************************************************************************************************
条款3.尽量用new和delete 不用 malloc和free
******************************************************************************************************************
条款5.对应的new和delete要采用相同的形式
delete stringptr1;// 删除一个对象
delete [] stringptr2;// 删除对象数组
******************************************************************************************************************
条款6:析构函数里对指针成员调用delete
除非对指针变量用了new, 否则是不需要用delete的
******************************************************************************************************************
条款11.为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
如果没重载操作符
string a("hello");
string b("world");
b=a;//b内存没释放,就指向了a
string a("hello"); // 定义并构造 a
{ // 开一个新的生存空间
string b("world"); // 定义并构造 b
b = a; // 执行 operator=
} // 丢失b的内存 // 离开生存空间, 调用 b的析构函数
string c = a; // c.data 的值不能确定! // a.data 已被删除
void donothing(string localstring) {}
string s = "the truth is out there";
donothing(s); //donothing()执行结束,localstring被删除,s包含一个指向 localstring早已删除的内存的指针。
*
只要类里有指针时,就要写自己版本的拷贝构造函数和赋值操作符函数
当实现拷贝构造函数和赋值操作符非常麻烦的时候,特别是可以确信程序中不会做拷贝和赋值操作的时候,只声明这些函数(声明为private成员)而不去定义(实现)它们
私有的拷贝构造函数,不支持对象拷贝
private:
CIniFile(const CIniFile&){}
******************************************************************************************************************
条款12: 尽量使用初始化而不要在构造函数里赋值
1)const成员只能被初始化,不能被赋值。
2)没有为CString 对象指定初始化参数,CString的缺省构造函数会被调用。然后再调用类构造函数赋值,导致两次调用CString函数
3)static类成员永远也不会在类的构造函数初始化
******************************************************************************************************************
条款13: 初始化列表中成员列出的顺序和它们在类中声明的顺序相同
规则:类成员是按照它们在类里被声明的顺序进行初始化的,和它们在成员初始化列表中列出的顺序没一点关系。
规则:对一个对象的所有成员来说,它们的析构函数被调用的顺序总是和它们在构造函数里被创建的顺序相反。
******************************************************************************************************************
条款14: 确定基类有虚析构函数
当通过基类对象去删除派生类对象时,如果基类对象没有虚析构函数,那么结果是不可预测的。此时派生类的析构函数不被调用。
当一个类作为基类时,需要虚析构函数,目的是让派生定义自己的析构函数
当一个类不作为基类时,不需要析构函数
****************************************************************************************************************--2016.3.16--
条款16: 在operator=中对所有数据成员赋值
1)*ptr = *rhs.ptr; // 对于ptr,赋的值是指针所指的值,不是指针本身
2)派生类调用基类的operator=
derived& derived::operator=(const derived& rhs)
{
if (this == &rhs) return *this;
static_cast<base&>(*this) = rhs; // 对*this的base部分,调用operator=,只是对derived对象的base部分赋值,转换的是base对象的引用,而不是base对象本身
y = rhs.y;
return *this;
}
3)派生类拷贝构造函数需要对基类也做拷贝,否则调用基类的缺省构造函数
derived(const derived& rhs): base(rhs), y(rhs.y) {}
******************************************************************************************************************--2016.3.22--
条款18: 类的接口完整 并且最小(少) (类要易于理解,易于使用,易于实现, 功能要强大,要方便使用)
函数的数量要尽可能地少,每一个函数都完成各自不同的任务.
不时增加函数以提供对各种通用功能的支持.
1)典型的接口里只有函数存在,因为在用户接口里放上数据成员会有很多缺点
2)一个完整的接口是指那种允许用户做他们想做的任何合理的事情的接口.
一个最小的接口,是指那种函数尽可能少、每两个函数都没有重叠功能的接口。
3)无端地在接口里增加函数是有代价的
4)友元函数在所有实际应用中都是类的接口的一部分。这意味着友元函数影响着类的接口的完整性和最小性。
****************************************************************************************************************** --2016.3.22--
条款19: 分清成员函数,非成员函数和友元函数
1)成员函数可以是虚拟的,而非成员函数不行(虚函数必须是成员函数)。
2)explicit的构造函数不能用于隐式转换。
3)如果需要的话,编译器会对每个函数的每个参数执行一种隐式类型转换。
4)能避免使用友元函数就要避免
const rational operator*(const rational& lhs,const rational& rhs)
{
return rational(lhs.numerator() * rhs.numerator(),lhs.denominator() * rhs.denominator());
}
5) 需要访问非公有成员的非成员函数只能是类的友元函数。
6) 只有非成员函数对最左边的参数进行类型转换。
******************************************************************************************************************--2016.3.22--
条款20: 避免public接口出现数据成员
1) 用函数来获取或设定数据成员的值,可以实现禁止访问、只读访问和读写访问等多种控制。甚至,如果你愿意,还可以实现只写访问。
2)public接口里放上数据成员无异于自找麻烦,所以要把数据成员安全地隐藏在与功能分离的高墙后
******************************************************************************************************************--2016.3.14--
条款21: 尽可能使用const
1) const出现在线的左边,指针指向的数据为常量;
2) const出现在线的右边,指针本身为常量;
3) const在现的两边都出现,二者都是常量。
4) 返回值是const: const rational operator*(const rational& lhs, const rational& rhs);
5) 参数值是const: const参数会导致一个临时对象的产生;
6) 函数本身是const:const成员函数的目的当然是为了指明哪个成员函数可以在const对象上被调用
7)仅在const方面有不同的成员函数可以重载。
char& operator[](int position)
{
return data[position];
}
const char& operator[](int position) const
{
return data[position];
}
8)修改一个“返回值为固定类型”的函数的返回值绝对是不合法的。返回值必须为对象的引用
9)const成员函数禁止修改它所在对象的任何一个数据成员。
数据意义上的const(bitwise constness)
概念意义上的const(conceptual constness)
******************************************************************************************************************--2016.3.14--
条款22: 尽量用“传引用”而不用“传值”
非常高效:没有构造函数或析构函数被调用,因为没有新的对象被创建。
const student& ReturnStudent(const student& s)
{ return s; }
避免了所谓的“切割问题(slicing problem)”
当一个派生类的对象作为基类对象被传递时,它(派生类对象)的作为派生类所具有的行为特性会被“切割”掉,从而变成了一个简单的基类对象。
引用几乎都是通过指针来实现的,所以通过引用传递对象实际上是传递指针。因此,如果是一个很小的对象——例如int——传值实际上会比传引用更高效。
******************************************************************************************************************--2016.3.23--
条款24: 在函数重载和设定参数缺省值间慎重选择
第一,确实有那么一个值可以作为缺省吗?
第二,要用到多少种算法?一般来说,如果可以选择一个合适的缺省值并且只是用到一种算法,就使用缺省参数(参见条款38)。
否则,就使用函数重载。
“缺省”构造函数是凭空(没有输入)构造一个对象,而拷贝构造函数是根据一个已存在的对象构造一个对象
在重载函数中调用一个“为重载函数完成某些功能”的公共的底层函数
******************************************************************************************************************--2016.3.23--
条款26: 当心潜在的二义性
C++有一种思想:它认为潜在的二义性不是一种错误。
******************************************************************************************************************--2016.3.18--
条款31: 千万不要返回局部对象的引用,也不要返回函数内部用new初始化的指针的引用
CPoint1 & operator=(const CPoint1& point)
{
CPoint1 cp1;
cp1.m_nX = point.m_nX*100;
cp1.m_nY = point.m_nY*100;
return cp1;
}
如果返回局部对象的引用,那个局部对象其实已经在函数调用者使用它之前被销毁了。
写一个返回废弃指针的函数无异于坐等内存泄漏的来临。
**************************************************************************************************************2016.3.24
条款30: 当数据成员是private或protect时,避免函数返回此数据成员的非const的 指针或引用
1)此时容易改变数据成员,破坏访问方式。
2)可以通过返回指向const对象的指针或引用来达到完美效果
3)当要返回private或protect数据成员时,需在函数名称前加上const。
******************************************************************************************************************
条款32: 尽可能地推迟变量的定义
将变量的定义推迟到必须使用它的时候,还要尽量推迟到可以为它提供一个初始化参数为止。
不仅可以避免对不必要的对象进行构造和析构,还可以避免无意义的对缺省构造函数的调用。而且,在对变量进行初始化的场合下,变量本身的用途不言自明,所以在这里定义变量有益于表明变量的含义。
******************************************************************************************************************
条款37: 决不要重新定义继承而来的非虚函数
任何条件下都要禁止重新定义继承而来的非虚函数。
******************************************************************************************************************
其他:
#define “***” ***的第一个字符不能为数字
#pragma warning(push,4)
******************************************************************************************************************
******************************************************************************************************************
More Effective C++ 笔记
******************************************************************************************************************
******************************************************************************************************************
条款1.限制某个 class 所能产生的对象数量
1)类中的静态成员总是被构造,即使不使用,而且你无法确定它什么时候初始化;
2)非成员内联函数在链接的时候在目标文件中会产生多个副本,可能造成程序的静态对象拷贝超过一个。
3)限制对象个数:建立一个基类,构造函数和复制构造函数中计数加一,若超过最大值则抛出异常;析构函数中计数减一。
4)允许一个或零个对象,阻止某个类的对象被创建的最简单方法就是把这个类的构造函数声明为私有(private)。
************************************************************************
条款2.限制某个 class 所能产生的对象数量了解 virtual functions、multiple inheritance、virtual base classes、runtime type identification 的成本
1)不要将虚函数声明为 inline ,因为虚函数是运行时绑定的,而 inline 是编译时展开的,即使你对虚函数使用 inline ,编译器也通常会忽略。
条款3.考虑使用其它程序库
选用不同的库可能会大幅改善程序性能,比如说 iostream 和 stdio 库.
******************************************************************************************************************
------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------
Effective C++ 笔记
------------------------------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------------------------------
******************************************************************************************************************
条款1.尽量用:const和inline 不用:#define
const char * const PRESCRIPTION_STATE_ = "正在配置";
static const int LIST_NUM = 5; //类中声明
char *szPatientArray[LIST_NUM]; //使用
const int 类名::LIST_NUM; //定义
enum{LIST_NUM = 5}
char *szPatientArray[LIST_NUM];
template<class T>
inline const T& Max(const T&a, const T&b)
{
return a>b ? a:b;
}
******************************************************************************************************************
条款2.尽量用#include<iostream> 不用:#include<stdio.h>
论 cin/cout 和scanf/print的优缺点
#include <iostream>得到的是置于名字空间std下的iostream库的元素
#include <iostream.h>得到的是置于全局空间的同样的元素,可能产生冲突
******************************************************************************************************************
条款3.尽量用new和delete 不用 malloc和free
******************************************************************************************************************
条款5.对应的new和delete要采用相同的形式
delete stringptr1;// 删除一个对象
delete [] stringptr2;// 删除对象数组
******************************************************************************************************************
条款6:析构函数里对指针成员调用delete
除非对指针变量用了new, 否则是不需要用delete的
******************************************************************************************************************
条款11.为需要动态分配内存的类声明一个拷贝构造函数和一个赋值操作符
如果没重载操作符
string a("hello");
string b("world");
b=a;//b内存没释放,就指向了a
string a("hello"); // 定义并构造 a
{ // 开一个新的生存空间
string b("world"); // 定义并构造 b
b = a; // 执行 operator=
} // 丢失b的内存 // 离开生存空间, 调用 b的析构函数
string c = a; // c.data 的值不能确定! // a.data 已被删除
void donothing(string localstring) {}
string s = "the truth is out there";
donothing(s); //donothing()执行结束,localstring被删除,s包含一个指向 localstring早已删除的内存的指针。
*
只要类里有指针时,就要写自己版本的拷贝构造函数和赋值操作符函数
当实现拷贝构造函数和赋值操作符非常麻烦的时候,特别是可以确信程序中不会做拷贝和赋值操作的时候,只声明这些函数(声明为private成员)而不去定义(实现)它们
私有的拷贝构造函数,不支持对象拷贝
private:
CIniFile(const CIniFile&){}
******************************************************************************************************************
条款12: 尽量使用初始化而不要在构造函数里赋值
1)const成员只能被初始化,不能被赋值。
2)没有为CString 对象指定初始化参数,CString的缺省构造函数会被调用。然后再调用类构造函数赋值,导致两次调用CString函数
3)static类成员永远也不会在类的构造函数初始化
******************************************************************************************************************
条款13: 初始化列表中成员列出的顺序和它们在类中声明的顺序相同
规则:类成员是按照它们在类里被声明的顺序进行初始化的,和它们在成员初始化列表中列出的顺序没一点关系。
规则:对一个对象的所有成员来说,它们的析构函数被调用的顺序总是和它们在构造函数里被创建的顺序相反。
******************************************************************************************************************
条款14: 确定基类有虚析构函数
当通过基类对象去删除派生类对象时,如果基类对象没有虚析构函数,那么结果是不可预测的。此时派生类的析构函数不被调用。
当一个类作为基类时,需要虚析构函数,目的是让派生定义自己的析构函数
当一个类不作为基类时,不需要析构函数
****************************************************************************************************************--2016.3.16--
条款16: 在operator=中对所有数据成员赋值
1)*ptr = *rhs.ptr; // 对于ptr,赋的值是指针所指的值,不是指针本身
2)派生类调用基类的operator=
derived& derived::operator=(const derived& rhs)
{
if (this == &rhs) return *this;
static_cast<base&>(*this) = rhs; // 对*this的base部分,调用operator=,只是对derived对象的base部分赋值,转换的是base对象的引用,而不是base对象本身
y = rhs.y;
return *this;
}
3)派生类拷贝构造函数需要对基类也做拷贝,否则调用基类的缺省构造函数
derived(const derived& rhs): base(rhs), y(rhs.y) {}
******************************************************************************************************************--2016.3.22--
条款18: 类的接口完整 并且最小(少) (类要易于理解,易于使用,易于实现, 功能要强大,要方便使用)
函数的数量要尽可能地少,每一个函数都完成各自不同的任务.
不时增加函数以提供对各种通用功能的支持.
1)典型的接口里只有函数存在,因为在用户接口里放上数据成员会有很多缺点
2)一个完整的接口是指那种允许用户做他们想做的任何合理的事情的接口.
一个最小的接口,是指那种函数尽可能少、每两个函数都没有重叠功能的接口。
3)无端地在接口里增加函数是有代价的
4)友元函数在所有实际应用中都是类的接口的一部分。这意味着友元函数影响着类的接口的完整性和最小性。
****************************************************************************************************************** --2016.3.22--
条款19: 分清成员函数,非成员函数和友元函数
1)成员函数可以是虚拟的,而非成员函数不行(虚函数必须是成员函数)。
2)explicit的构造函数不能用于隐式转换。
3)如果需要的话,编译器会对每个函数的每个参数执行一种隐式类型转换。
4)能避免使用友元函数就要避免
const rational operator*(const rational& lhs,const rational& rhs)
{
return rational(lhs.numerator() * rhs.numerator(),lhs.denominator() * rhs.denominator());
}
5) 需要访问非公有成员的非成员函数只能是类的友元函数。
6) 只有非成员函数对最左边的参数进行类型转换。
******************************************************************************************************************--2016.3.22--
条款20: 避免public接口出现数据成员
1) 用函数来获取或设定数据成员的值,可以实现禁止访问、只读访问和读写访问等多种控制。甚至,如果你愿意,还可以实现只写访问。
2)public接口里放上数据成员无异于自找麻烦,所以要把数据成员安全地隐藏在与功能分离的高墙后
******************************************************************************************************************--2016.3.14--
条款21: 尽可能使用const
1) const出现在线的左边,指针指向的数据为常量;
2) const出现在线的右边,指针本身为常量;
3) const在现的两边都出现,二者都是常量。
4) 返回值是const: const rational operator*(const rational& lhs, const rational& rhs);
5) 参数值是const: const参数会导致一个临时对象的产生;
6) 函数本身是const:const成员函数的目的当然是为了指明哪个成员函数可以在const对象上被调用
7)仅在const方面有不同的成员函数可以重载。
char& operator[](int position)
{
return data[position];
}
const char& operator[](int position) const
{
return data[position];
}
8)修改一个“返回值为固定类型”的函数的返回值绝对是不合法的。返回值必须为对象的引用
9)const成员函数禁止修改它所在对象的任何一个数据成员。
数据意义上的const(bitwise constness)
概念意义上的const(conceptual constness)
******************************************************************************************************************--2016.3.14--
条款22: 尽量用“传引用”而不用“传值”
非常高效:没有构造函数或析构函数被调用,因为没有新的对象被创建。
const student& ReturnStudent(const student& s)
{ return s; }
避免了所谓的“切割问题(slicing problem)”
当一个派生类的对象作为基类对象被传递时,它(派生类对象)的作为派生类所具有的行为特性会被“切割”掉,从而变成了一个简单的基类对象。
引用几乎都是通过指针来实现的,所以通过引用传递对象实际上是传递指针。因此,如果是一个很小的对象——例如int——传值实际上会比传引用更高效。
******************************************************************************************************************--2016.3.23--
条款24: 在函数重载和设定参数缺省值间慎重选择
第一,确实有那么一个值可以作为缺省吗?
第二,要用到多少种算法?一般来说,如果可以选择一个合适的缺省值并且只是用到一种算法,就使用缺省参数(参见条款38)。
否则,就使用函数重载。
“缺省”构造函数是凭空(没有输入)构造一个对象,而拷贝构造函数是根据一个已存在的对象构造一个对象
在重载函数中调用一个“为重载函数完成某些功能”的公共的底层函数
******************************************************************************************************************--2016.3.23--
条款26: 当心潜在的二义性
C++有一种思想:它认为潜在的二义性不是一种错误。
******************************************************************************************************************--2016.3.18--
条款31: 千万不要返回局部对象的引用,也不要返回函数内部用new初始化的指针的引用
CPoint1 & operator=(const CPoint1& point)
{
CPoint1 cp1;
cp1.m_nX = point.m_nX*100;
cp1.m_nY = point.m_nY*100;
return cp1;
}
如果返回局部对象的引用,那个局部对象其实已经在函数调用者使用它之前被销毁了。
写一个返回废弃指针的函数无异于坐等内存泄漏的来临。
**************************************************************************************************************2016.3.24
条款30: 当数据成员是private或protect时,避免函数返回此数据成员的非const的 指针或引用
1)此时容易改变数据成员,破坏访问方式。
2)可以通过返回指向const对象的指针或引用来达到完美效果
3)当要返回private或protect数据成员时,需在函数名称前加上const。
******************************************************************************************************************
条款32: 尽可能地推迟变量的定义
将变量的定义推迟到必须使用它的时候,还要尽量推迟到可以为它提供一个初始化参数为止。
不仅可以避免对不必要的对象进行构造和析构,还可以避免无意义的对缺省构造函数的调用。而且,在对变量进行初始化的场合下,变量本身的用途不言自明,所以在这里定义变量有益于表明变量的含义。
******************************************************************************************************************
条款37: 决不要重新定义继承而来的非虚函数
任何条件下都要禁止重新定义继承而来的非虚函数。
******************************************************************************************************************
其他:
#define “***” ***的第一个字符不能为数字
#pragma warning(push,4)