Effective C++读书笔记(条款1-10)

2024-08-25 10:18

本文主要是介绍Effective C++读书笔记(条款1-10),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

不得不说,Effective C++确实是一本C++进阶的好书,刚浏览完第二遍,

现对其做一个大体性的总结 ,并进行适当的展开,作为以后C++参考复习之用。


(一).让自己习惯C++

____________________________________________________________________________________________________________________________________

条款1:视C++为一个语言联邦

#1.将C++ 分为四个次语言:C,Templete C++, Object-Oritented C++, STL;

____________________________________________________________________________________________________________________________________

条款2:尽量用 const, enum, inline 替换 #define
#1.用const替代#define。
理由1:预处理器移走了记号名称,导致记号名称没进入符号表,当发生编译错误时,你只能得到定义值,因此不知道错误来自何处。
理由2:预处理器盲目的用定义值替换记号名称,会使目标码出现多份定义值,使目标码增大。

理由3:#defines不能用来定义class专属常量,而const可以。

#2.用enum替代#define
理由:#define对其后的编译过程都有效,而如果想把scope限定在类内,又要使用
编译前确定的常量,可以使用static const int 或 "the enum hack",如enum{Num=5;},
但在某些方面enum更接近#define的行为,比如enum和define一样不能取地址,

而const可以取地址,这可以避免pointer和reference指向你enum中的某个常量,实现约束。

#3.用inline替代#define
如:#define CALL_WITH_MAX(a,b) f((a) > (b)?(a):(b))
用CALL_WITH_MAX(++a, b);//a会被累加二次

换用inline function可享有这种宏带来的效率并且能正确执行。

____________________________________________________________________________________________________________________________________

条款3:尽可能使用const,因为这个标识符会告诉编译器增加约束
#1.bitwise constness是C++常量性的定义,编译器遵循这个规则,但有时候为了性能,可以考虑逻辑性的常量,巧用mutable实现客户端侦测不出的功能。
#2.为避免const和non-const重载的函数代码重复,可以利用non-const在转型中调用const来实现,反过来则不然,因为const函数的本意是不允许改变,不要破坏这个规则。

____________________________________________________________________________________________________________________________________

条款4:确定对象被使用前已先被初始化
#1.default构造函数初始化动作发生在进入构造函数本体之前,为确保效率性能,
对于自定义类型,请在成员初始列中初始化,而不是在构造函数本体中赋值。
#2.对应内置数据类型,为避免不明确行为和成员初始化的一致性,最好在成员
初始列中初始化
#3.若有多个构造函数,同时对多个内置数据类型初始化,为避免重复动作,可将
初始化赋值行为搬移到函数中,并同时调用该函数。
#4.为免除“跨编译单元初始化次序”问题,请以local static对象替换 non-local static 对象。
理由1:non-local static 对象被另一编译单元内某个 non-local static 对象用以初始化时,
因为编译单元次序不明确,所以无法保证前者已经被初始化。

理由2:local static 对象会在函数调用期间首次遇上时初始化,因此未使用时没有初始化成本。

____________________________________________________________________________________________________________________________________


(二).构造/析构/赋值运算

____________________________________________________________________________________________________________________________________

条款5:了解C++编译器默默编写并调用了哪些函数
#1.编译器暗自为class创建了default构造函数,copy构造函数,copy assignment操作符,以及析构函数。
#2.若class中含有const或reference成员变量,则调用默认的copy assignment的结果是
编译器拒绝编译那一行,除非你自己定义了copy assignment.
#3.如果base classes 将 copy assignment操作符声明为private, 编译器将拒绝为其

derived class 生成一个copy assignment操作符。

____________________________________________________________________________________________________________________________________

条款6:为驳回编译器自动生成机能,可将成员函数显示声明private,并不予实现
例如:
class HomeForSale{
public:...
private:HomeForSale(const HomeForSale&);HomeForSale operator=(const HomeForSale&);
}
private意味着企图拷贝HomeForSale对象时,会被编译器拒绝,若不慎
被member函数或friend函数调用,也会因其没有提供实现而被链接器抱怨。

若想将链接期错误转移至编译期,则可使用继承,如
class MyHomeForSale:private HomeForSale{...
};
此时尝试生成copy构造函数和copy assignment操作符时,会因调用base class对应

函数版本失败而被编译器拒绝。

____________________________________________________________________________________________________________________________________

条款7:为多态基类声明virtual析构函数
#1.带多态性质的base classes应该声明一个virtual析构函数,不带多态性质的base classes
不应该声明virtual析构函数, non-base class更不应该声明virtual析构函数(virtual会增加对象体积空间)。
(带多态性质意味着可能会有基类指针指向派生类对象)

#2.class中有virtual函数意味着带多态性质,因此也该拥有一个virtual析构函数。

____________________________________________________________________________________________________________________________________

条款8:别让异常逃离析构函数
#1.在两个异常同时存在的情况下,程序若不是结束执行就是导致不明确行为。
#2.别让析构函数吐出异常,如果一个被析构函数调用的函数可能抛出异常,析构
函数应该捕捉任何异常,然后它们(不传播),或结束程序,否则容易导致不明确行为。
#3.一种更好的策略是编写一个普通函数供客户调用,然后给客户一次捕捉异常的机会,
并在析构函数中对该普通函数执行判定操作,并在需要执行时捕捉函数异常,并实现#2的原则。
如:
class DBConn{
public:...
void close()
{db.close();bClosed = true;
}~DBConn
{if(!bClosed){try{db.close();bClosed = true;}catch(...){//制作运转记录,记下对close的调用失败...}}
}
private:DBConnection db;bool bClosed;
}

____________________________________________________________________________________________________________________________________

条款9:绝不在构造和析构函数中调用virtual函数
#1.确定你的构造函数和析构函数都没有(在对象被创建和销毁期间)调用virtual函数,
而它们调用的所有函数也都服从统一约束。
理由1:在derived class对象base class构造期间,并未初始化derived class专属成员,
调用derived class的函数会导致不明确行为,因此此时只是在调用base class的对应函数,
并无virtual真正意义。(析构函数同理)
理由2:derived class对象的base class构造期间,对象类型是base class而不是derived
class, 所以virtual 函数会被编译器解析至base class。另外,运行期类型信息dynamic_cast和
typeid也一样。(析构函数同理)
理由3:因为有上述理由,当在构造函数或析构函数中显示调用virtual函数时,某些编译器会
发出警告信息。

#2.为解决上述问题,可使用derived class的static函数将信息传递给base class构造函数或析构函数这项技术。
如:
class Transaction
{
public:explicit Transaction(const std:string& logInfo);void logTransaction(const std:string&logInfo) const;...
};
Transaction::Transaction(const std::string& logInfo)
{...logTransaction(logInfo);
}
class BuyTransaction:public Transaction{
public:BuyTransaction(parameters);:Transaction(createLogString(parameters)){...}...
private:static std::string createLogString(parameters);
};

____________________________________________________________________________________________________________________________________

条款10:令opertator=(+=,-=,*=类似运算操作符)返回一个reference to *this
理由1:reference比赋值在自定义类型中拥有更好的性能。
理由2:这一协议被所有内置类型和标准程序库提供的类型,如string,vector,complex,

tr1:shared_ptr等所遵守,为了统一起见,还是遵守吧!

____________________________________________________________________________________________________________________________________


这篇关于Effective C++读书笔记(条款1-10)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1105282

相关文章

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

6.1.数据结构-c/c++堆详解下篇(堆排序,TopK问题)

上篇:6.1.数据结构-c/c++模拟实现堆上篇(向下,上调整算法,建堆,增删数据)-CSDN博客 本章重点 1.使用堆来完成堆排序 2.使用堆解决TopK问题 目录 一.堆排序 1.1 思路 1.2 代码 1.3 简单测试 二.TopK问题 2.1 思路(求最小): 2.2 C语言代码(手写堆) 2.3 C++代码(使用优先级队列 priority_queue)

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给

2024/9/8 c++ smart

1.通过自己编写的class来实现unique_ptr指针的功能 #include <iostream> using namespace std; template<class T> class unique_ptr { public:         //无参构造函数         unique_ptr();         //有参构造函数         unique_ptr(