《编写高质量代码:改善C++程序的150个建议》读书笔记3
来源:互联网 发布:windows同步软件 编辑:程序博客网 时间:2024/06/02 19:41
0. 开始讲了一个比较有趣的事情,是关于VS编译器中经常会出现的两个汉字“烫”和“屯”。
看下面的示例:
int main() {puts((char*)malloc(100));return 0;}运行程序的结果如下:
再看编译产生的二进制,可以看到这个二进制里面有很多的烫。
原因主要是,在VS中,栈空间在未初始化的时候默认值是0xCC,而堆在未初始化的值是0xCD,而0xCCCC在GBK编码中对应的是“烫”,0xCDCD是“屯”。
1. new/delete和new[]/delete[]配对使用。
new和delete有两种形式,一种后面有[],一种没有。后者用来申请对象数组,具体格式是:
class CLS {public:CLS() {}CLS(int i) {}};int main() {CLS *c1 = new CLS();CLS *c2 = new CLS[3];}基本上,使用new[]只能使用默认的构造函数,比如上面的例子,如果没有CLS(){},编译就会失败。
另外,对于内置类型数组,使用delete和delete[]没有区别,不过还是建议使用delete[]。
int *a = new int[10];delete[] a;
2. new有三种形态。
1) new:这个就是我们平常使用的new。
2) operator new:这个是用来单独分配内存的,它其实就是一个普通的操作符。有一个全局的,另外还可以在类中重载。
3) placement new:这个用来在已有的内存上选择执行合适的构造函数。它是C++标准库的一部分,在<new>中声明,因此使用时要#include <new>。对应的有一个placement delete。
使用单独的new实际上会调用到后面的operator new和placement new,下面是使用operator new和placement new的例子:
#include <iostream>#include <new>using std::cout;using std::endl;class CLS {public:CLS() {}CLS(int i) {}};int main() {void *a = operator new(sizeof(CLS));CLS *c = static_cast<CLS *>(a);new(a)CLS(1);return 0;}new(a)CLS(1)就是placement new,它的声明在VS中位于vcruntime_new.h中:
#ifndef __PLACEMENT_NEW_INLINE #define __PLACEMENT_NEW_INLINE _Ret_notnull_ _Post_writable_byte_size_(_Size) inline void* __CRTDECL operator new(size_t _Size, _Writable_bytes_(_Size) void* _Where) throw() { (void)_Size; return _Where; } inline void __CRTDECL operator delete(void*, void*) throw() { return; }#endif下面是operator new的一个重载:
#include <iostream>#include <new>using std::cout;using std::endl;class CLS {public:CLS() {}CLS(int i) {cout << "CLS(int i)" << endl;}void* operator new(size_t size){cout << "operator new override" << endl;::operator new(size);}//这个也要写,不然new(a)CLS(1);报错void* operator new(size_t size, void *where){(void)size;return where;}//既然new重载了,delete最好也重载//略};int main() {void *a = operator new(sizeof(CLS));CLS *c = static_cast<CLS *>(a);new(a)CLS(1);return 0;}
以前new失败会返回NULL,这是为了跟C语言匹配。
后来改成抛出错误。
再后来又加了一个重载的operator new版本称为nothrow new,就是把抛出异常部分包装起来并返回了NULL。
所以用来处理new失败有两种方法:
class CLS {public:CLS() {}CLS(int i) {}};int main() {//方式1CLS *c = new (std::nothrow) CLS();if (NULL == c) {return -1;}//方式2try {CLS *c2 = new CLS();}catch (const std::bad_alloc &e) {(void)e;return -1;}}不过方式1只能保证operator new部分不出问题,但是如果之后的构造函数在执行时还是使用了没有std::nothrow版本的new,那么还会因为没有足够内存而抛出bad_alloc,这样的话后面的if(NULL == c)判断也就没有什么意义了。
4. new失败后并不会直接返回,而是与一个或多个new_handle()被执行。
new_handler的形式如下:(VS中的<new>文件)
#if !defined(_INC_NEW) || !defined(_MSC_EXTENSIONS)// handler for operator new failurestypedef void (__CLRCALL_PURE_OR_CDECL * new_handler) (); #endif /* !defined(_INC_NEW) || !defined(_MSC_EXTENSIONS) */new_handler大致做的事情有以下一些:
1) 尝试获取到更多的内存;
2) 使用set_new_handler安装下一个new_handler。因为operator new会在失败时执行多次,那么new_handler也可以执行多次,也一个new_handler里面安装另一个之后,下一次就会执行新安装的new_handler;
3) 到最后new_handler都没有效果,就直接装个NULL,实际上就不是卸载了new_handler;
4) 之后就抛出异常;
5) 或者直接abort()或者exit()结束程序。
另外,可以为不同的类实现自己的new_handler,这个先不深究。
5. 检测内存泄漏的一些工具,可以去了解下。
MS C-Runtime Library/BoundsChecker/Insure++/Rational Purify/Valgrind。
6. 重载operator new/delete的一些注意点。
为什么要重载?
因为系统默认的版本效率低,可能导致内存碎片化太严重;或者在某些特殊的应用下就需要自定义的版本。另外,重载后的版本还可以加入额外的功能,如检测代码中的内存错误或者获得内存使用的统计数据等。
如何实现?
1) 重载的opeartor new必须是全局函数或者类函数,如果是类函数,还必须是静态的,因为它需要独立于单个的类实例存在。(但实际上在VS2015中并不需要static,可以看上面的例子)
2) 一个全局opeartor new的例子:(heap_alloc()和CallNewHandler()还需要具体实现)
void* operator new(size_t size){if (0 == size)size = 1;//C++标准中规定,如果内存大小是0的时候也应该返回有效的内存地址void *res;for (;;) {res = heap_alloc(size);if (res)break;if (!CallNewHandler(size))break;}return res;}3) 一个全局opeartor delete的例子:
void operator delete(void *p) {if (NULL == p) {//C++标准规定删除一个NULL指针是安全的return;}free(p);}4) 重载opeartor new/delete的使用参数也可以变,只要保证第一个参数是size_t。
7. 使用智能指针。
这个是个需要深入研究的,这里先不展开。
8. 使用内存池来提供内存分配的效率。
- 《编写高质量代码:改善C++程序的150个建议》读书笔记3
- 《编写高质量代码:改善C++程序的150个建议》读书笔记1
- 《编写高质量代码:改善C++程序的150个建议》读书笔记2
- 《编写高质量代码:改善C++程序的150个建议》读书笔记4
- 《编写高质量代码:改善C++程序的150个建议》读书笔记6
- 读书笔记之《编写高质量代码:改善C#程序的157个建议》
- 《编写高质量代码:改善Java程序的151个建议》读书笔记二:基本类型
- 读书笔记--编写高质量代码 改善java程序的151个建议(一)基础
- 读书笔记--编写高质量代码:改善java程序的151个建议(四)String,StringBuilder,StringBuffer
- 读书笔记--编写高质量代码:改善java程序的151个建议(五)数组和集合
- 读书笔记--编写高质量代码 改善java程序的151个建议(六)枚举与注解
- 读书笔记--编写高质量代码 改善java程序的151个建议(八)异常
- 编写高质量代码--改善Java程序的151个建议--读书笔记
- 《编写高质量代码 : 改善C#程序的157个建议》读书笔记 1-10
- 《编写高质量代码 : 改善C#程序的157个建议》读书笔记 11-20
- 编写高质量代码-改善C++程序的150个建议-3
- 《编写高质量代码:改善C++程序的150个建议》读书笔记5(关于异常处理的部分)
- 《编写高质量代码:改善Java程序的151个建议》 建议3
- Qt在线讲座之QML脚本书写规范
- 子类化的MFC方式
- windows 下一台服务器多个tomcat服务安装
- 再水一发相同序列
- Web应用class寻址顺序
- 《编写高质量代码:改善C++程序的150个建议》读书笔记3
- 经验分享:php连接oracle及thinkphp 3.2.2连接oracle
- 25个经典的Spring面试问答
- 描述一下JVM加载class文件的原理机制
- poj3093(背包最大容量)
- Intellij Idea 将java项目打包成jar
- Codeforces 628E:Zbazi in Zeydabad 树状数组的奇妙用法
- jQuery图片上传前先在本地预览
- eclipse安装jad插件