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 析构函数
相关推荐
茯苓gao8 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾8 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
利刃大大8 小时前
【高并发内存池】五、页缓存的设计
c++·缓存·项目·内存池
DKPT9 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa9 小时前
HTML和CSS学习
前端·css·学习·html
ST.J9 小时前
前端笔记2025
前端·javascript·css·vue.js·笔记
C语言小火车9 小时前
【C++八股文】基础知识篇
c++·tcp/ip·const·智能指针·多线程同步·static关键字·c++内存模型
Suckerbin9 小时前
LAMPSecurity: CTF5靶场渗透
笔记·安全·web安全·网络安全
liulilittle9 小时前
IP校验和算法:从网络协议到SIMD深度优化
网络·c++·网络协议·tcp/ip·算法·ip·通信
眠りたいです9 小时前
基于脚手架微服务的视频点播系统-播放控制部分
c++·qt·ui·微服务·云原生·架构·播放器