天天看点

《Effective C++》读书笔记(四)七.模板与泛型编程八.定制new和delete九.杂项讨论

七.模板与泛型编程

模板以前只是学习的时候写过一些例子,并没有真正用过。本人对模板还处于超级小白的状态,基本看不懂这章,所以就先浏览一遍吧,等今后有经验之后,再回头看一下模板相关的内容。目前打算下一步学一下STL。

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

a)      classes和templates都支持接口和多态。

b)      对于classses中的接口是显式的,以函数签名为中心。多态通过virtual函数发生于运行期。

c)      Template中接口是隐式的,多态则通过template具现化和函数重载解析发生于编译期。

42.了解typename的双重意义:

a)      声明template参数时,前缀关键字class和typename等价。

b)      使用关键字typename标识嵌套从属类型名称,但不得在基类列或者初始化列表中作为base class修饰符。

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

44.将参数无关的代码抽离templates:

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

46.需要类型转化时请为模板定义非成员函数:

47.请使用traits classes表现类型信息:

48.认识tempate元编程:

八.定制new和delete

New和Delete关键字是C++最常用的关键字之一,也是最重要的内存分配相关的东东,当然有必要很好学习一下。

49.了解new-handler的行为:

a)      传统的new没有异常处理,如果内存分配失败,那么就返回null,而新的new在分配出错的时候,是会抛出异常的,为了满足一些老的程序,现在仍然有nothrow new,和老版本的new一样,但是这样做并不是很好,因为如果我们new一个对象,即使分配内存没有出错,但是构造函数一系列操作还是有可能有异常的。

b)      new-handler简单来说就是异常处理的一个函数,我们可以通过std::set_new_handler(函数指针),来设置new的异常处理函数。

c)      WindowsGDI编程的时候,使用不同的画笔或自体之前,先保存起来原来的字体资源(默认的),然后使用自己定义的,使用之后再换回原本的。关于new-handler也可以这样,先保存起来原本的new-handler,然后使用自定义的new-handler,最后再换回原来的。这样,我们就可以给不同的类定义不同的new-handler。

50.了解new和delete的替换时机:

这一条主要是讲我们为什么需要自己写一个new和delete,而不是使用C++自带的new和delete呢?主要有以下几点:

a)      用来检测使用时的错误,不管是什么,自己写的定制版总能增加一些额外的自己想要的功能,比如用来打印log。

b)      为了提升性能。C++自带的关键字new,虽然能够申请内存,但是这个new是需要照顾各种情况的,小内存,大内存,短周期,长周期都需要兼顾,所以对于单独的某种内存分配,可能并不是最好的选择。定制版的new可以加快分配速度,减少内存碎片等。

c)      定制版的new和delete可以收集程序内存使用的情况。

d)      保证数据类型齐位,有些机器比较苛刻,严格要求数据类型位对其,否则程序就会崩溃。有的即使不崩溃,也会降低运行效率。所以,保证数据齐位是很好的增加保险性与效率的方法。还有簇集的概念,当一组内容都位于一块内存时,就可以减少内存调页时缺页的次数,这也是增加效率的方法。

e)      虽然自定义的new和delete这么好,但是由于涉及到的内容还是很多,尤其是各种细节,所以,除非能力灰常强,还是不要自己写了,更好的办法是使用一些开源或者商业的库。

51.编写new和delete时要固守常规:

知道了为什么要自己定制new和delete之后,下面就看一下自己定制这两个操作时需要注意的地方。

a)      首先就是new操作符内部是一个死循环,申请内存成功时会跳出循环,如果失败的话,会调用new-handler,最后会抛出bad_alloc异常。

b)      new内部对于申请0字节内存采用的处理方式是改为申请1字节。

c)      基类的如果被继承了,那么使用new的时候,申请的大小很可能不是基类的大小,而是子类的大小。这时候如果不做处理的话肯定会出问题。可以改为如果大小不等于基类,那么直接改用::operator new(size)即原本的new进行处理。

d)      delete应该进行相关检测,如果要释放的指针为null,那么就不进行处理。

e)      关于delete也应该注意释放内容大于基类原本大小时的情况。也是可以调用::operater new(size)。但是,如果基类的析构函数不是virtual的话,可能传递给delete的size大小不正确,所以需要注意析构函数需要是virtual的。

52.写了placement new也要写placementdelete: 

a)      简单的说,就是不管干什么,new和delete都是一对好基友,有new的地方就要有对应的delete,而且需要严格对应哦!我们自己写一个new的时候,有可能加入placement,比如打印log等功能,这时候,就不能再使用原本的new了,需要找一个相应的delete和其对应。

b)      在我们覆写了new和delete时要注意名字的覆盖原则,我们在类中自己定义了new和delete时,原本的new和delete就会被覆盖。我们需要清楚这一点。如果还想用正常的话,可以使定义的new返回::new()。覆写这个类的时候,在类中using 基类::operator new。

九.杂项讨论

53.不要轻易忽略编译器的警告:

说来惭愧,能编过就OK,从不看warning的路过…

曾经听过一个笑话,在悬崖上立着一个牌子写着”warning”,大家走到那里都很小心,但是一个程序猿同志却跳下去了!虽然有点儿冷,不过事实确实如此,我们很大程度上忽略了编译器苦口婆心的warning,而这些warning确实可能很有用!、

a)      《Effective C++》的作者举了一个非const函数继承的时候没有覆写基类const函数的例子,这个确实非常非常难以发现。不过我写了一个例子,发现编译器并没有提醒我任何事,后来,我把警告等级调高之后,发现第一条就是提醒我没有覆写成功!看来,是时候看一看警告了,是时候将编译器的警告级别调高了,争取获得再高警告级别下也能写出少警告的代码。、

b)      不要过分依赖编译器的报警能力,同样的问题,有的编译器会报警,而有的却不会报警。不同的警告级别报告的内容也不同。所以,我们还是要自己提高代码的严谨性。

c)      当我们真正对警告完全了解的时候,才可以忽视警告,千万不能看都不看就忽视了!

54.让自己熟悉包括TR1在内的标准程序库:

C++学习到第二个阶段,也是时候该进行下一步的学习了。首先,要了解一下C++到底都有什么,语法熟悉了,常见的技巧也了解了,下面就是一些标准的程序库了,站在巨人的肩膀上才能看得更远!

a)      首先就是STL,这也是我下一步最想系统学习的一部分。本人是个算法渣渣,遇到数据结构就特别蛋疼,一直用的STL的一些基本的数据结构。感觉如果能系统的学习一下STL,今后在面对问题的时候就会有更多种选择。

b)      还有一些比如iostream,wchar_t,异常处理等等。

c)      TR1并不是一个标准,而只是一份文档,真正的东西还是在STL中和Boost中。

55.让自己熟悉boost:

之前就有听说过很多关于boost的东东,比如不依赖操作系统的线程库,shared_ptr等等。比如之后C++11标准之后就加入了多线程库,可见,boost库是C++标准库的后备军。

Boost中有好多好的东西,并且还在不断扩充,C++真的是博大精深,没有五年十年的时间,是不能掌握的。感觉未来的路很长,不过我喜欢!