Effective C++浅谈(一)

effective C++是一本很好的书籍,但由于翻译的影响,有时候读起来不是那么美好,所以在这整理了一份在实际开发过程中遇到的,个人认为重点的部分,希望各位同学和前辈进行指正,有问题可以提出,之后慢慢修改,C++基础知识不再过多解释,后续会补充C++11新特性的一些知识,希望大家共同进步

1、使用const、enum来替换#define

优点:

(1)const和enum在编译期间会有类型检查,define只有字符替换,并且在预处理期间替换完成,define没有类型检查

(2)const会被调试器识别,define不会

(3)const有作用域的限制,define没有

(4)enum会为每一个值分配一个类型,这样把enum赋值给非enum对象的时候,编译器会报错

(5)enum也会被调试器识别,define不会

(6)enum有作用域的限制,define没有

PS:使用inline替换define

(1)在制作宏函数的时候,inline函数可以对参数进行类型检查,define宏函数则没有类型检查

(2)inline可以被调试器识别,宏函数不会

(3)inline有作用域,宏函数没有

const、enum、inline可以调高代码的健壮性和可读性,但是define不能,无论何时,代码中出现比较多的define宏定义时,代码的可读性大大的被降低,阅读者会感到非常头疼

2、确定对象在使用前被初始化完成

(1)代码调试过程中,声明一个结构体,结构体中成员变量类型包括int、double等类型,在某些平台上,程序运行到这里时,可以看到声明完成后,对象内部的变量是一堆随机数,这在之后的使用过程中,会出现不可控的情况,极有可能导致程序崩溃,

(2)除了内置类型之外,剩余初始化的责任落在了构造函数的身上,需要确保构造函数对每一个成员变量进行了初始化

(3)构造函数对成员变量进行初始化是,应使用初始化列表进行初始化, 如果在构造函数内部进行初始化,本质上是一次赋值的过程,通常浪费成本

3、创建一个类时,如果是空类,系统会默认编写什么函数

构造函数、拷贝构造函数、析构函数、重载=运算符函数

4、如果不想使用这些默认编写的函数,应该明确的进行拒绝

将内置函数(拷贝构造函数等)声明为private,并且不去实现它(空实现),这样做的时候,可以将函数的参数中只写类型,不写参数,本来就没有意义,写不写都一样

class student

{

private:

        student(cosnt student & ){}

}

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

用于多态情况,当父类的指针指向子类的对象时,进行析构时,父类的析构函数不是virtual声明的,那么只会释放父类的空间,不会释放子类的空间,造成空间浪费,如果父类的析构函数声明为virtual时,调用父类的析构时,会先调用子类的析构函数,确保空间释放完成,在释放父类的空间,这样不会造成资源浪费

6、如果一个类不准备作为基类使用是,内部不要使用virtual进行声明管理

类内部存在virtual关键字时,就会创建一份虚表,虚表会占用一部分内存空间,使得本来的类空间增加,造成资源浪费

7、不要在构造或者析构函数中调用virtual函数

class Transaction

{

public:

        Transaction();

        virtual void logTransaction() const = 0

}

Transaction:: Transaction()

{

        ....

        logTransaction();

}

class BuyTransaction: public Transaction

{

public:

        virtual void logTransaction() const;

}

class sellTransaction: public Transaction

{

public:

        virtual void logTransaction() const;

}

执行

BuyTransaction b

会调用BuyTransaction的构造函数,但是会先调用Transaction的构造函数,基类的构造函数会在派生类构造之前调用,Transaction构造函数会调用logTransaction,这时候调用的是基类Transaction中的版本,在构造期间,virtual不会下降到派生类,并且在构造期间,virtual函数还不是virtual函数

8、重载=号返回一个对象 *this

9、重载=号中的自我赋值

在函数最前面添加一个证同测试,达到自我赋值的检验目的

if(this == &other) return *this

含义是,如果传入的就是对象本身,就不做任何事情

10、赋值对象时,不要忘记每一个成员

当一个对象中已经写好拷贝构造函数和一个重载=号函数时,函数运行不会再有问题,但是出现各位同学自己制作的拷贝构造函数时,编译器会生气!!,不会对拷贝构造函数做出任何的警告,为什么这么说,想想看,当你再向这个对象中添加一个成员变量,而又忘记在拷贝构造函数中对其进行赋值,或者在重载函数中漏掉新增成员变量,此时,编译器不会出现任何报错,也不会有任何提醒,相信经验丰富的同学都遇到过此种情况,执行拷贝之后,数据跑偏到外星球了,或者直接崩溃掉,如果此时再发生继承呢,后果不堪设想

11 资源管理,以对象管理资源

以往C风格的代码通常对申请空间的变量进行手动释放,比如new  delete,这是一种好的习惯,但是代码量越来越大的时候,不同的人去维护,可能会出现代码中间添加return,当然这是一种非常不好的编码习惯,非常不好!!!,会导致return在delete之前,这样资源一直被占用,这也是为什么代码跑起来,越来越卡,需要重启才能继续使用的一方面原因,同时,delete也有可能会出现多次调用的情况,如果同学们遇到过,一定体验过这种非凡的威力。。。。。(别问为什么我知道,这些死机问题,都体验过o(╥﹏╥)o)

把对象放进对象内,依赖析构函数自动调用的方式,确保资源被释放,这种方式叫RAII机制(resource acquisition is initialization)

在大型程序模型中一定会涉及到多线程操作,优秀的资源分配机制都是通过RAII锁机制来管理,非常方便,重点是安全、可靠

智能指针就是基于这种思想的指针(C++11新特性),注意auto_ptr这种不要使用了

12 new 和delete需要成对并且要采取相同形式

这个比较简单,不多废话了

13、引用传递代替值传递

函数参数都是以实际参数的副本为初值,调用端获得的是函数返回值的一个副本,出现副本一定会出现拷贝构造函数的调用

class Person

{

public:

        Person();

        virtual ~Person();

private:

        string name;

        string address;

}

class student : public Person

{

public:

        student();

        virtual ~student();

private:

        string name1;

        string address1;

}

bool func(student& s);

student ss;

bool b = func(ss)

这里我们来看一下一共进行了多少看不见的操作,ss对象内有两个string对象,每一次构造都会调用两次string类型的构造,ss继承Person对象,所以又增加连词string类型的构造,通过传值的方式,一个student会调用一次拷贝构造,一次Person的拷贝构造,四次string的拷贝构造,当函数的副本被销毁时,每一个调用构造的动作所对应的析构函数都会被调用,因此一共是六次构造和六次析构

这只是简单类型,如果是我们自己定义的复杂类型呢

所以使用引用来代替值传递,这样不会导致任何的构造函数或者析构函数被调用,没有任何的新对象被创建,如果func不会对引用进行操作,只进行读取,name建议加上const修饰

还有一种情况,避免切割的问题出现

但是遇到内置类型的时候需要特殊考虑,一般内置类型的传值效率比传递引用效率更高,这个同样适用于STL的迭代器和函数对象,原则上他们都是值传递效率更高,所以内置类型、STL的迭代器和函数对象请使用值传递,其他的情况使用引用代替值传递。

内置类型:int double float long等

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值