《Exceptional c++》和《提高c++性能的编程技术》学习笔记

1、c++临时对象

创建对象是一个费时,费空间的操作,会产生临时对象的几种情况:

1)以值的方式给函数传参
按值传递时,首先将需要传给函数的参数,调用拷贝构造函数创建一个副本,所有在函数里的操作都是针对这个副本的,也正是因为这个原因,在函数体里对该副本进行任何操作,都不会影响原参数。

指导原则:在传递函数参数时,选择以常量引用的方式,而不是传值方式

2)类型转换
我们在做类型转换时,转换后的对象通常是一个临时对象。

构造函数应该避免隐式类型转换,会隐含产生临时变量。用explicit constructor代替隐式转换

3)函数需要返回一个对象时
当函数需要返回一个对象,他会在栈中创建一个临时对象,存储函数的返回值。

4)对于大部分容器(包括链表)而言,调用容器的end()函数将返回一个临时对象,并且这个对象需要被构造和析构。由于这个临时对象的值在循环中是不会改变的,因此如果在每次循环迭代中都重新计算,将会导致不必要的开销,实际上这个临时对象只需计算一次,将其保存到局部对象当中即可。

5)使用后置++时会产生临时对象,应多使用前置++。

2、优先采用”a op= b;”而不是”a = a op b;”进行运算

例如,为什么operator += 的效率更高?原因是这个运算符是直接对其左边的对象进行运算,并且返回的是一个引用,而不是临时对象。与此不同的是,operator+必须返回一个临时对象。

通常,如果定义了某个运算符op,还应该定义其相应的赋值运算符op=,并且用后者来实现前者。

3、重载、覆盖和隐藏

1)对函数f()进行重载(overload)是指,在相同作用域中定义另一个相同名字的函数,并且与f()有着不同的参数类型、顺序或数目。
2)对虚函数f()进行覆盖(override)是指,在派生类中定义一个相同名字的函数,并且这个函数的参数列表与f()相同。
3)对外层作用域(基类、外部类或名字空间)中的函数f()进行隐藏(hide)是指,在内层作用域中(派生类、嵌套类或嵌套名字空间)定义另一个相同名字的函数,这将隐藏定义在外层作用域中的同名函数。

4、缓式构造

在C++中,不自觉地在程序开始出预先定义所有对象的做法是一种浪费。因为这样可能会创建一些直到最后都没有用到的对象。好的编码风格应该是在用到这个变量的时候再创建。
例如:

if(...)
{
XXXObject obj;
XXXFuc(obj);
}

在条件语句之后再创建变量可以减少开支。如果在之前就创建了变量,结果又没有用到,就会有额外的开支。

5、虚函数(模板对继承的优势)

内联是在编译时决定的,编译器不可能把运行时才解析的虚函数设置为内联。这往往会导致性能问题,解决方法:
因为函数调用的动态绑定是继承的结果,所以消除动态绑定的一种方法是基于模板的设计来替代继承。模板把解析的步骤从运行期提前到编译期,并且可以在编译时使用内联,提高了性能,而编译时间的适当增加是可以接受的。基于模板的设计有两个优点:重用和效率。

6、内存池

内存池(Memory Pool)是一种内存分配方式。 通常我们习惯直接使用new、malloc等API申请分配内存,这样做的缺点在于:由于所申请内存块的大小不定,当频繁使用时会造成大量的内存碎片并进而降低性能。

内存池则是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率得到提升。

7、内联

内联是用方法的代码来替换对方法的调用。内联通过消除调用开销来提升性能,并且允许进行调用间优化。内联的主要作用是对运行时间进行优化,当然他也可以使可执行映像变得更小。总结如下:

内联提升性能的两个方面:
1)调用间优化
调用间优化是面向某一方法的调用过程,基于对上下文场景更加全面的理解,使得编译器在源代码层面及机器代码层面对方法进行优化。这种优化的一般形式为:在编译期间进行一部分预处理,避免在运行时重复类似过程。
2)避免开销大的方法调用

内联缺点:1)代码膨胀。2)有些方法本身应避免内联,如递归。如果将递归函数A内联,编译器将不断循环尝试将A方法插入到A方法中,形成死循环。

8、引用计数

引用计数是内存管理的一个技巧,可以看做是一种简单的垃圾回收机制,它允许多个拥有共同值的对象共享同一个对象,省却了拷贝赋值的过程,更加节省内存和宝贵的CPU时间

引用计数的基本思想是将销毁对象的职责从客户端代码转移到对象本身。对象跟踪自身当前被引用的数目,而不具体记录是谁引用了它。在引用计数达到零时自行销毁 !!!降低了建立和解除引用的代价。。也就是,当对象不再被使用时自行销毁。引用计数提供了一种简洁高效的内存管理方法。

它简化了跟踪处理堆中对象的过程。一个对象被从堆中分配出来之后,我们需要明确的知道是谁拥有了这个对象,因为只有拥有这个对象的所有者能够销毁它。但我们在实际使用过程中, 这个对象可能被传递给另一个对象(例如通过传递指针参数),一旦这个过程复杂,我们很难确定谁最后拥有了这个对象。使用引用计数就可以抛开这个问题,我们不需要再去关心谁拥有了这个对象,因为我们把管理权交割给了对象自己当这个对象不再被任何人使用时,它自己负责销毁自己

总之,带有引用计数功能的智能指针兼有普通指针共享实值对象和auto_ptr自动释放实值对象的双重功能,并自动管理实值对象的生命周期和有效引用的计数,不会造成丢失引用、内存泄露和多次释放等问题。

但是,在引用计数的过程中,我们也丢失了重要的信息:到底是谁引用了自己。所以,引用计数在处理间接引用的问题上代价增加

9、上下文切换

上下文切换,有时也称做进程切换或任务切换,是指CPU 从一个进程或线程切换到另一个进程或线程。有较大时间开销。

这个过程要保存进程和处理器的状态。保存进程的状态是为了维护进程执行点的精确记录,保存处理器的状态是为了在相关进程继续执行时,处理器能返回原状态。每次进程换出时,与进程相关的处理器状态要从处理器移到内存里,每次进城换入时,处理器要把这部分数据加载进来。

上下文切换有三个主要开销:
1)处理器上下文迁移
2)缓存和TLB(旁路转换缓冲)丢失。每次上下文切换后,进程要重建它的缓存内容。
3)调度开销。调度器要决定是否继续执行中断的过程,或者是否把另外一个进程装载到处理器上。

10、

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值