对神做《Effctive c++》学习的一些总结和疑问(一)

概述

花时间通读了下Meyers大神的著做《Effective c++》,其中受益不少,毕竟书中有不少东西是以前在写代码时彻底没有考虑过的地方,做者用独到的眼光来告诉咱们,c++不是一门简单的编程语言,而是一门堆积埃菲尔铁塔式程序的艺术。c++

接下来,让我对书中的“条款”用本身的语言方式,做一些总结和我的心得批注。
(PS:其中带“*”号的条款是本人不太理解或者使人可能较难理解的条款。对其中一些难以理解的条款我会给予详细解释,简单条款将略过)编程

Part1: 在写c++时,“让本身习惯c++”:

1.c++能够分为:(1)C (2)Object-Oriented C++ (3)Template C++ (4)STL。编程语言

2.尽可能用const,enum,inline代替#define,或者说,宁肯用编译器代替预处理器。由于宏定义容易出错(思考define函数进行运算时须要加上小括号)。函数

3.尽量用const定义常量。ui

4.肯定对象在使用前已先被初始化。特别对于构造函数,最好用成员初值列(member initialization list),而不是在构造函数内使用赋值。this

举个例子:你有一个类A,那么在定义构造函数时,最好这样去初始化设计

cA:A(const string& name,const string& address,
const list<PhoneNumber>& phones):
theName(name),
thePhones(phones),
numTimesConsulted(0){}

这样的话,你无需对构造函数内部自己进行任何动做。理由在于,对大多数类型而言,这样比起调用默认构造函数高效许多。指针

Part2: 构造/析构/赋值运算

5.了解c++默默编写了和调用了哪些函数。code

就是好比说说你要清楚,c++在编译时会拒绝哪些赋值动做,拒绝哪些?对于一个class,编译器会默认为类建立default构造函数、析构函数、copy构造函数、copy assignment操做符。对象

6.若不想用编译器自动生成的函数,就拒绝他。好比,你能够把一个类的复制构造函数放在private里,在子类继承他时,使用私有继承,让类uncopyable。

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

对于一个多态基类而言,应该对他声明一个virtual析构函数,就是说,假如一个类带有任何的virtual函数,咱们就应该让他拥有一个virtual析构函数。

8.不要让析构函数吐出异常。若是有必要,那么在class中提供一个普通函数执行该操做。

9.绝对不要在构造和析构函数中调用virtual函数。

*10.令operatior=返回一个reference to *this。

cclass Widget{
......
    Widget& operator+=(const Widget& rhs) //返回类型是个reference,指向当前对象
    {
        ...
        return *this;
    }
......
}

11.在operator=中处理“自我赋值”。你不能保证用户不会让对象作自我赋值这种看起来虽然愚蠢的事情。

*12.确保复制对象时没有忘记他的每个成员。包括全部的公有与私有成员。

Part3: 资源管理

13.用对象来管理资源。
这里推崇一个概念--RAII(Resource Acquisition Is Initialization),你在得到得到一个对象时,必须对他进行相应有效的管理,使用STL提供的auto_ptr或者shared_ptr能让你更加轻松使用对象。推荐使用shared_ptr,无须担忧复制动做带来的麻烦。

*14.当心资源管理的copy行为。
--对RAII对象作到禁止复制。
--对底层资源使用引用计数法(reference-count),好比在写锁操做时。
--对复制操做进行深拷贝(考虑堆的深复制)。
--转移底层资源的全部权。当一个对象被复制,资源的拥有权将从被复制的对象转移到目标对象上。

*15.在资源管理类中提供对原始资源的访问。

16.new与delete时采用相同形式。

S *s1 = new S;
delete S;
S *s2 = new S[100];
delete []s2;

*17.以独立语句将newed对象储存于智能指针中。

int priority();
void processWidget(std::tr1::shared_ptr<Widget> pw, int priority);

//如今调用processWidget
processWidget(new Widget, priority());

如今你会发现这个代码没法经过编译,由于tr1::shared_ptr须要一个原始指针,但他的构造函数是个explict构造函数。

如今你把他修改为:

processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());

可是,这样会有很大可能致使内存泄漏。
思考,当咱们对priority的调用失败时,咱们没法阻止内存泄漏的产生!
避免方案其实很简单,就以下,用一个独立语句拆分他。

std::tr1::shared_ptr<Widget> pw(new Widget);
processWidget(pw, priority());

Part4: 设计与声明

18.让接口容易被正确使用,不易被误用。

*19.设计class犹如设计type。

20.宁用pass-by-reference-to-const代替pass-by-value

class Person(){
public:
    Person();
    virtual ~Person();
    ...
private:
    string name;
    string address;
};

class Student():public Person{
public:
    Student();
    ~Student();
    ...
private:
    string schoolName;
    string schoolAddress;    
};

bool validateStudent(Student s);
Student plato;
bool platoIsOk = validateStudent(plato);

当你用这个方法去传递一个Student对象时,整体成本是六次构造函数和六次析构函数。(本身算下string对象和student对象的创造过程)

(未完待续,最近实习入职,有时间继续写)

相关文章
相关标签/搜索