Effective C++
小瓶子的笔记本
这个作者很懒,什么都没留下…
展开
-
【Effective C++】用成员模板函数接受所有兼容的类型
问题我们知道,指针有一个很好的点,就是能够在类继承层次之间支持隐式转换,derived class 指针能够被隐式转换为 base class 指针,如下面这样:class Top {};class Middle : public Top {};class Bottom : public Middle {};Top* pt1 = new Middle(); //Middle* 被转换为 Top*Top* pt2 = new Bottom(); //Bottom* 被转换为 Top*const原创 2020-09-07 20:50:09 · 237 阅读 · 0 评论 -
【Effective C++】处理模板化基类中的名称
引例假设现在有这样一个例子:class CompanyA {public: void sendClearText(const std::string& msg); void sendEncryted(const std::string& msg); //...};class CompanyB {public: void sendClearText(const std::string& msg); void sendEncryted(const std::st原创 2020-08-30 15:46:49 · 116 阅读 · 0 评论 -
【Effective C++】typename 双重含义
含义一:模板声明在 template 的声明式中,可以使用 class 或者 typename,如下:template<class T> class Widget;template<typename T> class Widget;在这里,class 和 typename 没有任何不同,个人倾向于使用 typename,避免与类型声明的 class 混淆。含义二:声明嵌套从属名称类型变量假设现在有这样一个模板函数,传进来一个模板容器,希望打印出容器内的第二个元素:原创 2020-08-30 12:17:46 · 184 阅读 · 0 评论 -
【Effective C++】谨慎使用 private 继承
引例:考虑 Person,Student 继承体系,Student 以 private 继承 Person:class Person { //... };class Student : private Person { //... };void eat(const Person& p);void study(const Student& s);void main(){ Person p; Student s; eat(p); eat(s);}这个时候,eat原创 2020-06-15 11:54:42 · 305 阅读 · 0 评论 -
【Effective C++】对象之间的复合(composition)关系 —— has a 以及 is-implemented-in-terms-of
复合关系当某种类型的对象内含其他种类型的对象时,它们之间便是这种复合关系。比如:class Address { //... };class PhoneNumber { //... };class Person {public: //...private: std::string name; Address address; PhoneNumber voiceNumber; PhoneNumber faxNumber;};在这里例子中,Person 对象就由 std::strin原创 2020-06-10 16:44:27 · 364 阅读 · 0 评论 -
【Effective C++】不要重新定义继承而来的缺省参数值
现在考虑这样一种继承体系:class Shape{public: enum ShapeColor {Red, Green, Blue}; virtual void draw(ShapeColor color = Red) const = 0; //...};class Rectangle : public Shape{public: virtual void draw(ShapeColor color = Green) const;};class Circle : publi原创 2020-06-10 16:00:47 · 171 阅读 · 0 评论 -
【Effective C++】virtual function 的替代方案
假设你正在为一个游戏软件中的人物设计一个继承体系。每个人物都有一个血量值,且不同的人物之间血量的变化方式也不一样,基于这样的目的,你可能会想着这样设计一个基类:class GameCharacter{public: virtual int healthValue() const; //返回人物当前的血量 //...};具体的人物类会继承这个 GameCharacter 基类,可以在其中对血量的计算函数 healthValue() 进行重写。在基类中,并没有将 healthValue() 函数原创 2020-06-06 11:42:45 · 214 阅读 · 0 评论 -
【Effective C++】区分接口继承和实现继承
当你在设计一个 class 的继承体系时,对于基类的成员函数,你可能会希望 Derived class :只继承基类成员函数的接口能够同时继承基类成员函数的接口和实现,但是又希望能够 override能够同时继承基类成员函数的接口和实现,且不允许 override这三种不同的基类成员函数实现方式,实际上就对应了基类成员函数的三种不同形式:pure virtual function,impure virtual function 和 non-virtual function。假设现在有如下的继承原创 2020-06-05 10:41:56 · 203 阅读 · 0 评论 -
【Effective C++】避免隐藏继承而来的名称
这个主题可能更多的是和作用域有关。先来看看下面这种简单的情形:int x;void func(){ double x; std::cin >> x; //读的是 local double x variable}这里,cin 读取的就是 local 的 x 变量,而不是 global 的 x 变量。因为内层作用域的名称会屏蔽掉外围作用域的名称,即使它们的类型并不相同。那在继承体系下,又是怎样的情形呢?继承体系下的作用域问题现在有下面这种继承结构:class Bas原创 2020-06-04 12:26:07 · 227 阅读 · 0 评论 -
【Effective C++】将文件之间的编译关系降到最低
C++ 的编译依赖关系如果你对 C++ 的某个实现文件做了修改,你可能会发现,所有与这个类有关的其他项目文件都需要重新编译。这主要归咎于 C++ 并没有把 “将接口与实现分离” 这件事情做得很好。比如说像下面这样:#include<string>#include"date.h"#include"address.h"class Person{ //对外接口public: Person(const std::string& name, const Date& b原创 2020-06-03 17:39:30 · 184 阅读 · 0 评论 -
【Effective C++】彻底了解 inline 函数
inline 函数有着众多优点:看起来像是函数,使用起来也像是函数,但是又没有函数调用的额外开销比宏好用得多,具体见 这篇文章方便编译器执行语境相关最优化。因为大部分编译器会对那些“不含函数调”用的代码进行优化。所以当你 inline 某个函数,或许编译器就可以对调用它的函数进行语境相关的最优化。当然天下没有白吃的午餐,inline 函数也有它自己的缺点:对 inline 函数的每一次调用,都会用 inline 函数本体进行替换。这样就有可能增加目标代码的大小,甚至会导致额外的换页行为,引起原创 2020-06-02 15:41:46 · 374 阅读 · 0 评论 -
【Effective C++】尽量写出“异常安全”的代码
异常安全代码的目标当函数抛出异常时,具有“异常安全”性的代码应该实现以下两点:不泄露任何资源。不破坏数据。 也就是说,系统处于前后一致的状态。比如下面这个代码:class Image;//多线程环境下使用class Menu{private: std::mutex mtx; //互斥器 Image* bgImg; //当前的背景图案 int imgChanges; //图案被改变的次数public: //改变背景图像 void ChangeBackground(st原创 2020-05-29 11:40:24 · 298 阅读 · 0 评论 -
【Effective C++】避免返回 handles 指向对象内部成分
首先抛出结论:不要返回对象私有成员的 handles ,handles 指引用,指针和迭代器。这样可以增强类的封装性,避免可能出现的 const 成员函数不 const (即外部仍然可以通过 const 成员函数修改对象内部私有数据),以及 dangling handles 的问题 (即空指针或空引用)。返回私有成员指针在下面 LeftBottom 和 RightTop 函数中,我们是想要客户端获得 Rectangle 对象的左下角和右上角的点坐标。因为在这两个函数内部,并没有修改对象内部数据,原创 2020-05-27 11:47:16 · 320 阅读 · 0 评论 -
【Effective C++】尽量少做转型动作
转型语法转型通常有三种形式:C 风格的转型动作:(T) expression函数风格的转型动作:T( expression )C++ 风格的转型动作,其中又提供了四种操作,各有不同的目的。 C++ 风格的四种转型操作const_cast用来移除对象上的常量性 (const) 或者是易变性 (volatile)static_cast主要用于非多态类型之间的转换用于基本数据类型之间的转换,比如说 int 转换为 char,double 等用于类的继承层次结构中,基类和子类之间指原创 2020-05-20 09:50:52 · 179 阅读 · 0 评论 -
【Effective C++】考虑写出一个不抛出异常的swap函数
首先抛出结论:下面再来讲讲原因:swap含义及缺省实现swap 函数是用来将两个对象的指彼此赋予对方。在缺省情况下,swap的动作可以由STL中提供的 swap 算法来完成,它的实现也很简单:namespace std{ template<typename T> void swap(T& a, T& b) { T temp(a); a = b; b = temp; }}只要 T 支持拷贝构造和拷贝赋值, 缺省的 swap 就会为你完成置换 T原创 2020-05-17 10:16:01 · 294 阅读 · 0 评论 -
【Effective C++】以独立语句将 new 出来的对象放入到智能指针
首先抛出结论:当你 new 出来一块内存,并且想把他们放入到智能指针当中时,最好使用一条独立语句,否则可能会发生内存泄露。下面再来讲讲原因:考虑有这样一个函数 ProcessWidget:class Widget;int CalPriority();void ProcessWidget(std::shared_ptr<Widget> pw, int priority)...原创 2020-04-08 11:02:31 · 269 阅读 · 0 评论 -
【Effective C++】operator= 拷贝赋值操作注意事项
首先抛出结论:让 operator= 返回一个 reference to *this要在 operator= 中处理“自我赋值”要在 operator= 中处理“异常”,不可盲目的将当前内存直接删除,防止分配新内存失败让 operator= 返回 reference to *this主要是基于以下两点考虑:1. 减少不必要的拷贝构造函数的调用。可以对比分析一下直接返回对象和返回...原创 2020-04-06 12:10:17 · 187 阅读 · 0 评论 -
【Effective C++】不要在构造函数和析构函数中调用 virtual 函数
首先结论如标题所示。如果你有一个类的继承体系,用来模拟股票交易市场的买进卖出,并且每一笔交易都需要进行记录,那么可能会有如下的类继承关系:class Transaction {public: Transaction() { //... LogTransaction(); } virtual void LogTransaction() const = 0;};clas...原创 2020-04-05 12:22:28 · 178 阅读 · 0 评论 -
【Effective C++】不要让异常逃离析构函数
首先先抛出结论:析构函数绝对不要抛出异常。如果在一个析构函数中某个函数可能调用失败,抛出异常,这个析构函数应该捕捉这个异常,然后“吞下”他们,或者结束程序。如果客户端需要对某个操作函数运行期间抛出的异常做出反应,那么这个 class 应该提供一个普通函数接口,而不是在析构函数中自己处理异常。假如有以下的代码片段:class Widget {public: ... ~Widget...原创 2020-04-05 10:40:30 · 182 阅读 · 0 评论 -
【Effevtive C++】尽量以 const enum inline 替换 #define
以 const 或者 enum 替换 #define 常量定义在 C 代码中,我们可能会用以下宏定义去定义一个常量:#define N 2这种方法只是在预编译的时候进行了字符替换,也就是会将所有 N 出现的地方替换为 2。在预编译之后,程序中将不会再有 N 这个标志。N不是变量,并不占内存单元,容易出错。比如下面这个例子:int n = 2;#define N1 n + n#defi...原创 2020-04-02 12:54:32 · 262 阅读 · 0 评论