Effective C++ 学习笔记一

Effective C++ 学习笔记一

文章目录

尽量以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 成员函数

  1. 他们使class的接口容易被理解,可以知道那个函数可以改动对象的内容,那些函数不行
  2. 他们使"操作const对象"成为可能,我们有const成员函数可以用来处理取得的const对象
  3. 成员函数只有才不更改对象的任何成员变量(static) 除外,才可以说是const
  4. const成员函数绝不更改对象的逻辑状态,non-static成员函数却没有这般承诺

确定对象使用前已经被初始化

  1. 读取未初始化的值会导致不明确的行为,在某些平台上,仅仅只是读取未初始化的值,可能会导致程序终止
  2. 别混淆赋值和初始化
cpp 复制代码
A::A(String a,String b){   这是赋值
    theA = a
    theB = b
}

---
初始化:可以使用成员初值列替换赋值动作
A::A(String a,String b):theA(a),theB(b){   这是初始化
}
  1. 基于赋值的那个版本首先调用default构造函数为theA,the设置默认的初值,在紧接着对他们立刻赋予新值,因此default构造函数做的一切都浪费了,成员初值列的做法避免了这一个问题,
  2. 规定总是在初值列表中列出所有成员变量,以免遗漏成员变量,许多class拥有多个构造函数,,每个构造函数有自己的成员初值列,如果这种classes存在许多成员变量或者base classes ,多份成员初值列表会导致重复,可以使用一个private的 inti方法,将重复的初始化移到init里面 在构造函数里面调用init。
  3. 为避免"跨编译单元之初始化次序"问题,可以用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析构函数。

请记住:

  1. 带多态性质base class 应该声明一个virtual 析构函数,如果class 带有任何virtual 函数,他就应该拥有一个virtual 析构函数
  2. class 的设计目的如果不是作为base class 使用,或不是为了具备多态性质,就不应该声明virtual 析构函数
相关推荐
做人不要太理性几秒前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.9 分钟前
2、桥接模式
c++·桥接模式
chnming198713 分钟前
STL关联式容器之map
开发语言·c++
VertexGeek16 分钟前
Rust学习(八):异常处理和宏编程:
学习·算法·rust
程序伍六七26 分钟前
day16
开发语言·c++
小陈phd44 分钟前
Vscode LinuxC++环境配置
linux·c++·vscode
二进制_博客1 小时前
Flink学习连载文章4-flink中的各种转换操作
大数据·学习·flink
火山口车神丶1 小时前
某车企ASW面试笔试题
c++·matlab
codebolt1 小时前
ADS学习记录
学习
是阿建吖!1 小时前
【优选算法】二分查找
c++·算法