Effective C++学习笔记

Effective C++

1.让自己习惯C++ 2.构造/析构/赋值运算

命名习惯

lhs(left-hand-side) rhs(right-hand-side)

“指向一个T型对象”的指针命名pt,意思是“pointer to T”

尽量以const,enum,inline替换#define

class GamePlayer{
private:
    enum { NumTurns = 5 };//"the enum hack"-令NumTurns成为5的一个记号名称
    int scores[NumTurns];
}

尽可能使用const

如果关键字const出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量 ;如果出现在星号两边,表示被指物和指针两者都是常量

两个成员函数如果只是常量性不同,可以被重载

为多态基类声明virtual析构函数

当derived class对象经由一个base class指针被删除,而该base class带着一个non-virtual析构函数,其结果未有定义--实际执行时通常发生的是对象的derived成分没被销毁

vptr(virtual table pointer)

被让异常逃离析构函数

pure virtual函数导致abstract(抽象)classes -- 也就是不能被实体化(instantiated)的class

//如果close抛出异常就结束程序。通常调用abort完成:
DBConn::~DBConn()
{
    try{db.close();}
    catch(...){
        制作运转记录,记下对close的调用失败;
        std:abort();//abort--中止
    }
}

绝不在构造和析构过程中调用virtual函数

base class构造期间virtual函数绝不会下降到derived classes阶层

RTTI(Run-Time Type Identification,运行时类型识别)在C++中,为了支持RTTI提供了两个操作符:dynamic_cast和typeid。dynamic_cast允许运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全地转化类型

由于无法使用virtual函数从base classes向下调用,在构造期间,可以藉由“令derived classes将必要的构造信息向上传递至base class构造函数”替换至而加以弥补。令此函数(必要的构造信息)为static,也就不可能意外指向“初期未成熟之derived class对象内尚未初始化的成员变量”

在operator=中处理“自我赋值”

在复制pb所指东西之前别删除pb

copy-and-swap

3.资源管理

以对象管理资源

把资源放进对象内,便可依赖C++的“析构函数自动调用机制”确保资源被释放

auto_ptr是个“类指针(pointer-like)对象”,也就是所谓智能指针;性质:若通过copy构造函数或copy assignment操作符复制它们,它们会变成null,而复制所得的指针将取得资源的唯一拥有权

auto_ptr和shared_ptr两者都在其析构函数内做delete而不是delete[]动作

在资源管理类中提供对原始资源的访问

.get()

auto_ptr和shared_ptr都提供一个get成员函数,用来执行显式转换,也就是它会返回智能指针内部的原始指针(的复件)

operator T() const 隐式转换函数

以独立语句将newed对象置入智能指针

4.设计与声明

宁以pass-by-reference替换pass-by-value

1.无构造、析构成本 2.避免slicing(对象切割)问题

对内置类型以及STL的迭代器和函数对象,值传递往往比较适当

必须返回对象时,别妄想返回其reference

将成员变量声明为private

“语法一致性”(用不用括号)、“细微划分之访问控制”、封装(改动之后的影响)

愈多东西被封装,愈少人可以看到它。而愈少人看到它,我们就有愈大的弹性去变化它,因为我们的改变仅仅直接影响看到改变的那些人和事物

private(提供封装)和其他(protected、public)不提供封装

宁可以non-member non-friend函数替换member函数

这样做可以增加封装性、包裹弹性(packaging flexibility)和机能扩充性

若所有参数皆需类型转换,请为此采用non-member函数

result = onehalf * 2;           //很好
result = 2 * onehalf;          //错误!

通常我们不能够(不被允许)改变std命名空间内的任何东西,但可以(被允许)为标准templates(如swap)制造特化版本,使它专属于我们自己的classes(例如Widget) “template<>”特例化

考虑写一个不抛异常的swap函数

模板分为类模板与函数模板,特化分为全特化与偏特化,函数模板不能偏特化

当打算偏特化一个function template时,惯常做法是简单地为它添加一个重载版本(但std不能再添加了)

总之,如果swap缺省实现码对你的class或者class template提供可以接受的效率,你不需要额外做任何事。任何尝试置换(swap)那种对象的人都会取得缺省版本,那将有良好的运作。
​
其次如果swap缺省版本实现的效率不足(那几乎总是意味着你的class或者class template,使用了某种pimpl手法)试着做以下事情:
​
1. 提供一个public swap成员函数,让它高效地置换你的类型的两个对象值。
​
2. 在你的class或者class template所在的命名空间提供一个non-member swap,并令它调用上述swap成员函数。
​
3. 如果你正编写一个class(而非class template),则为你的class特化std::swap,并令它调用你的swap成员函数。
​
最后,如果你调用swap,请确定包含一个using申明式,以便让std::swap在你的函数内曝光可见,然后不加任何namespace的修饰,赤裸裸地调用swap。

5.实现

尽可能延后变量定义式的出现时间

避免不必要的构造、析构成本

尽量少做转型动作

单一对象(例如一个类型为Derived的对象)可能拥有一个以上的地址(例如“以Base*指向它”时的地址和“以Derived指向它”时的地址),这种情况下会有个偏移量(offset)

避免返回handles指向对象内部成分

避免返回handles(包括references、指针、迭代器)指向对象内部。遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”(dangling handles)的可能性降至最低

为”异常安全“而努力是值得的

以对象管理资源、reset 、copy and swap

透彻了解inlining的里里外外

inline函数背后的整体观念是,将“对此函数的每一个调用”都以函数本体替换之。

inline意味“执行前,先将调用动作替换为被调用函数的本体”。

将文件间的编译依存关系降至最低

制作Handle class的两个办法:

1、使用pimpl idiom(pimpl,“pointer to implementation”)

2、令成为一种特殊的abstract base class(抽象基类),称为Interface class。

6.继承与面向对象设计

避免遮掩继承而来的名称

名称遮掩规则,即使base classes和derived classes内的函数有不同的参数类型也适用,而且不论函数是virtual或non-virtual一体适用。可以使用using声明式转交函数(函数体内...使用Base::)达成目标

区分接口继承和实现继承

 

考虑virtual函数以外的其他选择

non-virtual interface(NVI)手法:令客户通过public non-virtual成员函数间接调用private virtual函数

typedef std::tr1::function<int (const GameCharacter&)> HealthGalcFunc;

 

绝不重新定义继承而来的non-virtual函数

绝不重新定义继承而来的缺省参数值

virtual函数系动态绑定(dynamically bound),而缺省参数值却是静态绑定(statically bound)。

静态绑定又名前期绑定,early binding;动态绑定又名后期绑定,late binding。

静态类型 动态类型可以表现出一个对象将会有什么行为。

通过复合塑模出has-a或“根据某物实现出”

当复合发生于应用域内的对象之间,表现出is-a的关系;当它发生于实现域内则是表现is-implemented-in-terms-of(根据某物实现出)的关系。

Set对象可根据一个list对象实现出来。

明智而审慎地使用private继承

Private继承意味implemented-in-terms-of。它通常比复合(composition)的级别低。但是当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,这么设计是合理的。

derived classes可以重新定义virtual函数,即使它们不得调用它。

EBO(empty base optimization:空白基类最优化);C++裁定凡是独立(非附属)对象都必须有非零大小

明智而审慎地使用多重继承

factory function(工厂函数)--创建对象

“public继承自某接口”、“private继承自某实现”

7.模板与泛型编程

了解隐式接口和编译期多态

对template参数而言,接口是隐式(implicit)的,奠基于有效表达式,并不基于函数签名式。多态则是通过template具现化和函数重载解析发生于编译期。类似于“哪一个重载函数应该被调用”(发生在编译期)和“哪一个virtual函数该被绑定”(发生在运行期)之间的差异。

了解typename的双重意义

template内出现的名称相依于某个template参数,称之为从属名称(dependent names)。如果从属名称在class内呈嵌套状,称之为嵌套从属名称(nested dependent name)。

typename std::iterator_traits<T>::value_type temp(*iter);//iterator_traits
//"类型为T之对象所指之物的类型",如果T是vector<int>::iterator,temp的类型就是int
//value_type被嵌套于iterator_traits<T>之内而T是个template参数,所以必须在它之前放置typename
typedef typename std::iterator_traits<T>::value_type value_type;

请使用关键字typename标识嵌套从属类型名称;但不得在base class lists(基类列)或member initialization list(成员初值列)内以它作为base class修饰符。

学习处理模板化基类内的名称

编译器往往拒绝在templatized base classes(模板化基类)内寻找继承而来的名称,不进入base class作用域内查找。

可在derived class templates 内通过“this->”指涉base class templates内的成员名称,或藉由一个明白写出的“base class资格修饰符”完成(using)。

将与参数无关的代码抽离templates

共性与变性分析(commonality and variability analysis)

 

scoped_array

scoped_array与scoped_ptr基本是一对孪生兄弟,它包装了new[]操作符(而不是new)在堆上分配的动态数组,为动态数组提供了一个代理(Proxy),保存正确地释放内存。它弥补了标准库中没有指向数组的智能指针的遗憾。
    
scoped_array与scoped_ptr区别基本不大,主要特点如下:
1,构造函数接受的指针p必须是new[]的结果,而不是new表达式的结果;
2,没有*、->操作符重载,scoped_array持有的不是一个普通指针;
3,析构函数使用delete[],而不是delete;
4,提供operator[]重载,可以像普通数组一样使用下标访问元素;
5,没有begin(),end()等类似容器迭代器操作函数。
    
scoped_array管理的是动态数组,而不是单个动态对象,通常创建方式是的。scoped_array<int> sa(new int[100]); //包装动态数组
既然使用重载了下标操作符那么自然可以这样使用,sa[0] = 10;
但是注意,它不提供指针运算,所以不能用数组首地+N的方式访问数组元素,如*(sa + 10) = 10;错误用法,无法通过编译
scoped_array不提供数组索引的范围检查,如果使用了超过动态数组大小的索引或是负数索引将会引发未定义行为。

运用成员函数模板接受所有兼容类型

但是,同一个template的不同具现体(instantiations)之间并不存在什么与生俱来的固有关系(译注:这里意指如果以带有base-derived关系的B,D两类型分别具现化某个template,产生出来的两个具现体并不带有base-derived关系)

//成员函数模板(member function templates)
//泛化(generalized)copy构造函数
template<typename T>
class SmartPtr{
    public:
    template<typename U>
    SmartPtr(const SmartPtr<U>& other);
    ...
}

如果声明member templates用于“泛化copy构造函数”或“泛化assignment操作”,还是需要声明正常的copy构造函数和copy assignment操作符。

需要类型转换时请为模板定义非成员函数

 

请使用traits classes表现类型信息

STL共有5中迭代器分类:Input、Output、forward、Bidirectional、random access

Traits classes使得“类型相关信息”在编译期可用。它们以templates和“templates特化”完成实现。p232

认识template元编程

Template metaprogramming(TMP,模板元编程)

 

8.定制new和delete 留待以后再看

了解new-handler的行为

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值