Effective C++ 学习笔记一
文章目录
- [Effective C++ 学习笔记一](#Effective C++ 学习笔记一)
-
- 尽量以const enum inline 替换#define
- 尽可能使用const
-
- [const 成员函数](#const 成员函数)
- 确定对象使用前已经被初始化
- 了解C++默默编写并调用那些函数
- 若不想使用编译器自动生成函数,就该明确拒绝
- 为多态基类声明Virtual的析构函数
尽量以const enum inline 替换#define
cpp
#define ASPECT_RATIO 1.653 由于这个记号可能未被编译器看见,也许在编译器开始处理源码之前就
被预处理器移走,就会导致这个记号名称 ASPECT_RATIO 有可能没有进入记号表
--- 解决:
用一个常量替换上述的宏(#define)
const double AspectRatio = 1.653 语言常量肯定会被编译器看到,当然就会记入符号表
- 对于单纯的常量,最好是以const对象或enums 替换#define
- 对于形似函数的宏,最好改用inline函数替换#define
cpp
#define CALL_WITH_MAX(a,b) f((a)>(b) > (a) : (b))
---替换
template<typename T>
inline void callWithMax(const T& a, const T& b)
{
f(a>b?a:b);
}
尽可能使用const
定义一个语义约束指定一个不被改动的对象,编译器会强制实施这项措施,你可以在class外部修饰 gloabl或者namespace 作用域中的常量,或者修饰文件,函数,或者区作用域中被生命为static的对象。
:::info
如果const 出现在*左边 表示被指物事常量
如果const出现在* 右边则表示指针自身是常量
如果出现*两边 则表示被指物和指针两者都是常量
:::
const 成员函数
- 他们使class的接口容易被理解,可以知道那个函数可以改动对象的内容,那些函数不行
- 他们使"操作const对象"成为可能,我们有const成员函数可以用来处理取得的const对象
- 成员函数只有才不更改对象的任何成员变量(static) 除外,才可以说是const
- const成员函数绝不更改对象的逻辑状态,non-static成员函数却没有这般承诺
确定对象使用前已经被初始化
- 读取未初始化的值会导致不明确的行为,在某些平台上,仅仅只是读取未初始化的值,可能会导致程序终止
- 别混淆赋值和初始化
cpp
A::A(String a,String b){ 这是赋值
theA = a
theB = b
}
---
初始化:可以使用成员初值列替换赋值动作
A::A(String a,String b):theA(a),theB(b){ 这是初始化
}
- 基于赋值的那个版本首先调用default构造函数为theA,the设置默认的初值,在紧接着对他们立刻赋予新值,因此default构造函数做的一切都浪费了,成员初值列的做法避免了这一个问题,
- 规定总是在初值列表中列出所有成员变量,以免遗漏成员变量,许多class拥有多个构造函数,,每个构造函数有自己的成员初值列,如果这种classes存在许多成员变量或者base classes ,多份成员初值列表会导致重复,可以使用一个private的 inti方法,将重复的初始化移到init里面 在构造函数里面调用init。
- 为避免"跨编译单元之初始化次序"问题,可以用local static 对象替换non-local static 对象,local static对象是指在函数中定义的static对象,non-local static 对象反之,如果直接使用non-local static对象,比如non-local static a 去调用 non-local static b 但是 不确认调用a的时候 b是否被初始化。因为C++对"定义于不同的编译单元内的non local static 对象的初始化并无明确的定义。解决方案:
了解C++默默编写并调用那些函数
编译器可以暗自为class 创建默认的构造函数,copy构造函数,copy assignment操作符,以及析构函数。
若不想使用编译器自动生成函数,就该明确拒绝
对于一个类 如 HomeForSale 你不想其他人复制他,但是有如下代码:
cpp
HomeForSale h1;
HomeForSale h2;
HomeForSale h3(h1); // 企图通过拷贝 h1 不应该通过编译
h1 = h2;// 企图通过拷贝 h2 不应该通过编译
但是系统会自动化帮我们生成copy函数和copy assignment操作符 所以默认就会支持上述操作,但是这里是需要
禁止copying
答案是:
将copy函数和copy assignment操作符声明成private 的,由一个明确声明的成员函数,阻止编译器自动为你
创建的默认函数,使用private 禁止其他人调用
但是这个方法并不绝对安全 member函数和friend函数 还是可以调用你的private函数的,但是可以故意将
其函数声明为private 而不去实现他们。
还有一种更优的办法,为了阻止copying的动作专门设计一个base class 内 ,
cpp
class UnCopyable
{
protected:
UnCopyable(){}
~UnCopyable()();
private:
UnCopyable(const UnCopyable&);
UnCopyable& operator=(const UnCopyable&);
}
我们唯一需要做的就是 继承UnCopyable就行
class HomeForSale: private UnCopyable{
}
为了驳回编译器自动提供的机能,可将相应的成员函数申明成private 并且不予实现。使用像UnCopyable这种base class 也是一种做法
为多态基类声明Virtual的析构函数
设计一个TimeKeeper base class 和 一些derived class 作为不同的计时方式:
cpp
class TimeKeeper{
public:
TimeKeeper();
~Timekeeper();
···
};
class AtomicClock:public TimeKeeper{····};
使用工厂模式返回一个对象:
TimeKeeper* ptk = getTimeKeeoper();
delete ptk;
// 从TimeKeeper 继承体系获得一个动态分配对象 运用它 释放它 ,避免资源的泄露。
问题出在 getTimeKeeper返回的指针指向一个derived class , 而那个对象却经由一个base class指针
被删除,而目前的base class 有一个non-virtual 析构函数, 这会造成一定的灾难。
问题原因:
C++ 明白指出 , 当derived class 对象经由一个base class 指针被删除,而该base class 带着一个non-
virtual析构函数,其结果未有定义,实际执行时,通常该对象的derived 成分没有被销毁,然而其base class
成分会被销毁,于是造成诡异的"局部销毁"对象。这可是造成资源泄露,败坏数据结构。
消除这个问题的办法 : 给base class 一个vitual 的析构函数 ,此后删除derived class 就会销毁整个对象
无端的将所有的classes的析构函数声明为virtual,就像从未声明他们为virtual一样,都是错误的。许多深得人心的做法:只有当class 内含一个virtual函数才为他们声明virtual析构函数。
请记住:
- 带多态性质base class 应该声明一个virtual 析构函数,如果class 带有任何virtual 函数,他就应该拥有一个virtual 析构函数
- class 的设计目的如果不是作为base class 使用,或不是为了具备多态性质,就不应该声明virtual 析构函数