【XZ理解】Effective C++【1】基本原则

1-视C++为一个语言联邦类似于大纲类的谈谈概要
原因:

解决方式:

实际情况:


2-尽量以Const,Enum,Inline代替#define
原因:
宁可以编译器代替预处理器。#define不被视为语言的一部分,这正是问题所在。
解决方式:
1.对于单纯的常量,最好以const和Enum代替#define。

2.对于形似函数的宏,最好采用Inline函数代替。
使用形似函数的宏是因为其高效性,不需要带来函数招用的额外开销。InLine函数既可以打到宏的效率,也可以满足函数的所有可预料行为和类安全性。
实际情况:
当前代码中有很多此类型问题,带来了许多不必要的开销。


3-尽可能使用Const重看
原因:
你告诉某个对象是不可改变的,编译器帮你保证。
const出现在*号左边,代表所指物是常量。出现在*右边,便是指针是常量。出现在两边表示所指物和指针都是常量。
解决方式:
。。。
实际情况:
当前代码中有很多此类型问题,带来了许多不必要的开销。


4-确定对象在使用前被初始化
原因:
1.处理内置类型(语言自带的类型int,float等)以外的其他东西,初始化责任都落在构造函数,要确保构造函数对对象的每一个成员都初始化。
2.不要混淆初始化和赋值操作,赋值操作对于非内置类型会有额外消耗(空跑一次构造函数或者默认构造函数)。
3.当no-local static对象A构造函数依赖其他的non-local static对象B,会存在对象B在A之后初始化的问题。no-local static对象的初始化顺序无法控制。
解决方式:
1-对内置类型进行手工初始化,因为C++不保证初始化他们。
2-构造函数最好使用成员初值列,而不要在构造函数内使用赋值操作。初值列列出的排列顺序应该与class中申明次序相同。
3-为避免“跨编译单元之初始化次序”问题,请以local static对象替换no-local static对象。
例如 no-local static对象
extern FileSystem tfs;
改为 local static对象
FileSystem  & tfs()
{
    static FileSystem fs;
    return fs;
}
实际情况:
当前代码中有很多此类型问题,带来了许多不必要的开销。


5-C++默默编写并调用了哪些函数
原因:
编译器暗自为class创建了如下函数:
1.默认构造函数。Empty e1;
2.析构函数。
3.拷贝构造函数。Empty e2(e1);
4.拷贝操作符复用。e2 = e1;
解决方式:

实际情况:


6-如果不想使用编译器自动生成的函数,就应该明确拒绝
原因:

解决方式:
1.为了拒绝编译器暗自提供的机能,可将相应的成员函数声明为private,并且不与实现。
2.或者定义一个Base Class类UnCopyable,并且将相应的成员函数声明为private,并且不与实现
实际情况:


7-为多态基类声明virtual析构函数
原因:
1.多态基类没有virtual析构函数,可能导致销毁不充分。
Base Class:
TimeKeeper
class AtomicKeeper : public TimeKeeper
class WaterKeeper : public TimeKeeper
工厂函数:
TimeKeeper * getTimeKeeper();
TimeKeeper * ptk = getTimeKeeper();//返回一个AtomicKeeper  *
....
delete ptk;
如果TimeKeeper的析构函数不是Virtual,delete ptk会导致局部销毁的情况,只销毁了TimeKeeper部分,没有销毁AtomicKeeper  部分
2.过度定义virtual函数会导致对象的体积增加。
Virtual函数的存储和调用原理:
(因为欲实现virtual函数,对象必须携带某些信息,用于决定在运行期哪一个virtual函数被调用。这份信息通常由一个vptr指针(virtual table pointer)指出。vptr指向一个由函数指针构成的数组,称为vtbl(virtual table)。当对象想调用某一个virtual函数,实际被调用的函数取决于该对象vptr指向的vtbl,编译器在其寻找适当的函数指针)
解决方式:
1.多态性质(polymorphic)的base class应该申明一个virtual析构函数。
2.如果class带有任何virtual函数,它就应该拥有一个virtual析构函数。
3.class的设计目的不是为了多态,就不应该声明virtual析构函数。额外的定义virtual函数,会导致对象体积增加。
4.如果你想要继承base class中含有non-virtual析构函数,请不要继承。很遗憾C++没有禁止派生的能力。
std::string,STL容器vector,list,set等含有non-virtual析构函数的类,最好不要派生。
实际情况:
应该有很多该加的没加


8-别让异常逃离析构函数
原因:

解决方式:
析构函数绝对不要吐出异常。在析构函数捕获异常做出相关处理,吞下错误或者终止程序。
实际情况:


9-不要在构造函数和析构函数中调用virtual函数
原因:
在base class的构造函数构造期间,virtual函数不是virtual函数。
此时的virtual函数只是base class里面的virtual函数,而不是派生类derived class的virtual函数。
反过来说,如果C++允许你在base class的构造函数构造期间,用派生类derived class的virtual函数,这样可能会涉及到派生类derived class未被初始化的局部变量,带来危险和不确定。

反之对于析构函数也是一样的道理。
解决方式:
将virtual函数改为no-virtual函数,并且派生类derived class的构造函数传递必要参数给对于的no-virtual函数。
实际情况:


10-领operator=返回一个Reference to *this
原因:
int x,y,z;
x=y= z=15;
x=(y=(z=15));
为了实现连锁赋值,赋值操作符必须要一个Reference指向操作符左侧的实参。
解决方式:
1.这个协议是用于所有内置类型(int,float等)和STL类型(List,map等)。
2.这个协议还适用于=,+=,-=,*=等赋值相关运算。
3.如果自己定义一个新类,也应该符合这个原则。
实际情况:


11-在operator=中处理"自我赋值"
原因:
显式自我赋值:
Widget w;
w=w;
隐式自我赋值:
a[i]=a[j];----如果i=j
*px=*py;-----如果*px和*py指向同一个东西。
解决方式:
1.确保当对象自我复制时,operator=有良好的行为。其中即使包括比较“来源对象”和“目标对象”地址、精心周到的语句顺序、以及copy-and-swap。
2.确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为依然正确。
实际情况:


12-复制对象时勿忘其每一个成分
原因:
宁可以编译器代替预处理器。#define不被视为语言的一部分,这正是问题所在。
解决方式:
1.Copy拷贝构造函数应该确保复制“对象内所有成员变量",要包括base class的成分。
2.不要尝试以某个copying函数实现另外一个copying函数。应该将共同机能放进第三个函数中,并由两个copying函数共同调用。
实际情况:



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值