《C++应用程序性能优化::第五章动态内存管理》学习和理解

说明:《C++应用程序性能优化》 作者:冯宏华等 2007年版。

2010.8.29

cs_wuyg@126.com

  这一章不错,之前对new和delete的理解并不是很深。虽然学C++primer的时候懂一点智能指针,但是没学过boost的智能指针,这书里边都讲到了,找了下资料,很容易理解。这本书适合有一定开发经验的开发人员,我这个还没有开发经验的菜鸟还是能从中获得很多知识。

一、             operator new/delete

  C++标准中3.7.3中规定,C++实现通过全局“allocation functions”new/new []和“deallocation functions”delete/delete[]来提供动态内存的访问和管理。

众所周知,new试图分配给定大小size的内存。如果成功,返回获得内存块的起始地址。这里需要指出的是,C++标准中没有规定是否要对获得的内存进行初始化。这意味着,如果开发人员没有显式的对获得的内存赋值,那么它们的初始值完全取决于编译器的实现。

  operator new有多种形式,placement new(例如char *p = new(buff) char[100])可以指定要申请的空间在哪个地方(就在buff里)。

  应用程序必须处理内存分配失败的情况。通过可以通过捕获std::bad_alloc异常或者返回值检查内存分配是否成功,而更好的方法是使用C++中的new_handler()函数。

  C++标准中对内存分配失败有明确的规定,系统会调用当前安装的new_handler()函数。这个错误处理函数是通过set_new_handler()安装到系统上的,C++规定new_handler要执行下列操作中的一种:

(1)       使new操作有更多的内存可用,然后返回

(2)       抛出一个bad_alloc或其派生的异常

(3)       调用abort()或者exit()退出。

二、             自定义operator new/delete

  当应用程序需要用同一的机制来控制数据的内存分配情况,并且不想使用系统提供的内存管理机制时,可以通过重写自己的全局operator new/delete来实现。一般对于一些内存要求较高的应用程序可能会采用这种方式。此外,有时为了调试内存分配情况,也会实现全局的operator new/delete,增加一些特殊处理,如产生log等来方便调试和排错。《Effective C++》中指出,自己重写operator new/delete时,很重要的一点就是函数提供的行为要和系统默认的operator new一致。

  当自定义全局的operator new/delete时,程序中的所有内存分配释放将使用统一的方式。然而在某些情况下,程序希望创建不同的类对象时使用不同的内存分配方式,尤其是在一些要求内存分配效率的程序中。为此,可以通过类成员函数形式的operator new/delete重载来为一些特定的类实现这个类自己的operator new/delete,而其他则保持使用系统默认的operator new/delete。在实际开发中,如果要自定义operator new,最好也要实现自定义delete。

  当某个类自定义了operator new/delete,它的派生类也会默认继承使用基类自定义的operator new/delete。如果想让派生类使用全局的new/delete,那么可以在基类的operator new中判断参数的大小,当是基类的时候就使用自定义的,当不是基类的时候就是用全局的。也可以通过另一种方式实现,让派生类重写operator new,在重写的函数里边调用全局的new。

三、             避免内存泄漏

  一个复杂的C++程序中最容易出现的问题是内存泄漏。即是忘记释放申请的内存,造成程序占用的内存不断上升,系统性能会不断下降,甚至会内存耗尽而导致程序崩溃。相比java,C++没有自动的垃圾回收,但是C++语言还是提供了足够强大和灵活的机制。可以将new和delete操作封装到一个类里边,在构造时分配内存,在析构时释放内存,这样就不会有内存泄漏(资源申请、释放跟构造、析构绑定,这是RAII:resource acquisition is initialization)。

  把new和delete操作封装到类里边,要考虑到拷贝构造函数和重载赋值操作的问题,因此还需要增加一些支持。因为当使用默认的拷贝构造函数时,执行位拷贝,使得两个不同对象里边的指针指向同一个堆区的空间,这样当某个对象析构的时候,就会释放掉该堆空间,这样就出问题了,可能同一个空间被释放两次。同样赋值操作也存在这样的问题,而且赋值操作的右操作数对象的堆空间可能没有被释放。

  一种解决的办法是把拷贝构造函数和赋值操作私有了,使得赋值或使用拷贝构造函数的操作都会被编译器认为不合法。这样就不会出现两个不同的对象指向同一个堆地址空间(boost的scoped_ptr就是这样),也就是说不同的对象不会共享同一个堆地址空间。将那两个函数私有了,会存在很多的限制。

  另一种解决方法是,增加一个引用计数,用于统计有多少个对象指向某块相同的堆区域,刚构造一个对象时就在堆上分配一块空间,并让引用计数值为1,当发生拷贝构造,或者该对象作为右值对另一个对象赋值时就让引用计数加1,而不会去分配堆空间。同样的当某个对象不再指向那块堆区域时,引用计数就减1。对象的析构操作和赋值操作都有可能会使得对象的引用计数减为0,所以在这两个函数里边必须判断什么时候引用计数为0,一旦为0,就释放那块堆区域,不为0就不释放。

四、             智能指针

  通过对申请和释放内存的封装是避免内存泄漏的基本思路,要使得有通用性、方便还有很多要考虑。现在的很多C++库提供了称为“智能指针”的模板类。使得开发人员可以方便的管理动态内存,而不必担心内存泄漏的问题。

  智能指针就是存储指向动态内存/对象的指针和类,其使用和工作机制与C++内置的指针非常相似,而最大的不同是会在适当的时间自动删除指向的内存和对象。此外,一般还提供reset()方法,可以显式释放内存或销毁对象。它们使用operator->和operator*来生成原始指针,这样智能指针看上去就像一个普通指针。但它们要考虑很多因素,例如所有权、线程安全,以及异常安全问题等。

  std::auto_ptr的使用。如果发生拷贝,那么被拷贝的源对象对象所指向的堆空间所有权就归目的对象所有,也就说如果一个对象被另一个对象拷贝了,那么那个被拷贝的对象就是“空”的了。不可以用于数组、STL容器。

  boost::scoped_ptr的使用。可以通过reset()显式销毁对象。与std::auto_ptr不同,不会发生所有权转移,因为它把拷贝构造函数和赋值重载放到了私有成员里。不可以用于数组、STL容器。

  boost::scoped_array的使用。是scoped_ptr的数组形式扩展。

  boost::shared_ptr的使用。可拷贝、可赋值。类似于之前的使用了“引用计数”的类。可以通过reset()来解除某个对象对某堆空间区域的指向,这样就使得引用计数减1。可以使用use_count()方法检查当前指向所管理的对象的shared_ptr的个数。

  boost::shared_array的使用。支持共享指针的数组形式。会使用delete[]来释放对象。

  boost::weak_ptr的使用。跟shared_ptr配合使用,是一种可以在shared_ptr使用之前进行安全检查的类。可以用于多线程的安全访问。weak_ptr的lock()方法返回一个shared_ptr对象,如果该对象存在,则可以使用返回的对象,不然返回的是空指针。

  正确的使用智能指针,可以减少开发人员的错误,提高开发效率;如果使用不正确,也将给程序带来灾难。这里的关键是正确地理解每种智能指针的语义,了解其优点和限制。然后结合程序的需求,选择合适的智能指针或者避免使用智能指针。

另外,“侵入式智能指针”跟上边说到的不是同类,可以了解下。

    长时间的看同一本书,有些烦躁。

2010-8-30

cs_wuyg@126.com

  因为网络问题,没法下载到boost库的安装文件。还好Code::Blocks绿色版里边有.hpp文件和编译好的.lib。。

附上书上的或另外找的学习代码:

1、new_handler的使用

//new_handler的使用.cpp
//2010.8.28
//coder:cs_wuyg@126.com
//参考P151
//Code::Blocks vs
#include <iostream>
using   namespace   std;
/
char *gPool = NULL;
void    my_new_handler();
/
int main()
{set_new_handler(my_new_handler);gPool = new char[100*1024*1024];if (gPool != NULL){cout << "Preserve 101MB memeory at " << hex << (void*)gPool << endl;}char *p = NULL;for (int i = 0; i < 20; ++i){p = new char[100*1024*1024];cout << i+1 << "*100M, p = " << hex << (void*)p << endl;}cout << "Done" << endl;return 0;
}
/
/*new_handler函数*/
void my_new_handler()
{if (gPool != NULL){cout << "try to get more memory" << endl;delete[] gPool;gPool = NULL;return;}else{cout << "I can not help..." << endl;throw bad_alloc();}return ;
}
/*
Preserve 101MB memeory at 00430020
1*100M, p = 06840020
2*100M, p = 0CC50020
3*100M, p = 13060020
4*100M, p = 19470020
5*100M, p = 1F880020
6*100M, p = 25C90020
7*100M, p = 2C0A0020
8*100M, p = 324B0020
9*100M, p = 388C0020
a*100M, p = 3ECD0020
b*100M, p = 450E0020
c*100M, p = 4B4F0020
d*100M, p = 51900020
e*100M, p = 57D10020
f*100M, p = 5E120020
10*100M, p = 64530020
11*100M, p = 6A940020
12*100M, p = 70D50020
try to get more memory
13*100M, p = 00430020
I can not help...
*/

2、placement new的使用

//placement new.cpp
//2010.8.28
//coder:cs_wuug@126.com
//参考P153
//Code::Blocks10.05  VS
#include <iostream>
using   namespace   std;int main()
{char buffer[1024];char *p = new(buffer) char[20];cout << "buffer: " << (void*)buffer << endl;cout <<"p : " << (void*)p << endl;system("pause");return 0;
}
/*
buffer: 0012FB68
p : 0012FB68
请按任意键继续. . .
*/

3、自定义类operator new,delete

//自定义类operator new,delete.cpp
//2010.8.28
//coder:cs_wuyg@126.com
//参考P160
//自定义类operator new/delete的使用,基类使用了自定义的new、delete但是派生类不使用自定义的new、delete
//Code::blocks VS
#include <iostream>
using   namespace   std;
/
class Base
{public:int m_Value;static void* operator new(size_t n);static void* operator new(size_t n, char* file, int line);
};void* Base::operator new(size_t n, char* file, int line)
{cout << "size: " << n << endl<< "new at " << file << ", " << line << endl;return ::operator new(n);
}
void* Base::operator new(size_t n)
{cout << "size: " << n << endl;return ::operator new(n);
}class Derived : public Base
{int m_nDerivedValue;public:static void* operator new(size_t n);//重写static void* operator new(size_t n, char* file, int line);//重写
};void* Derived::operator new(size_t n, char* file, int line)
{return ::operator new(n);
}
void* Derived::operator new(size_t n)
{return ::operator new(n);
}
/
int main()
{cout << "----------test1----------" << endl;Base *p1 = new Base;Base *p2 = new(__FILE__, __LINE__) Base;cout << "----------test2----------" << endl;Derived *p3 = new Derived;Derived *p4 = new(__FILE__, __LINE__) Derived;char *buffer = new char[10];system("pause");return 0;
}
/*
----------test1----------
size: 4
size: 4
new at D:\C++应用程序性能优化学习笔记\第五章动态内存管理\自定义类operator new,delete.cpp, 51
----------test2----------
请按任意键继续. . .
*/

4、“引用计数”类

//引用计数类.cpp
//2010.8.28
//coder:cs_wuyg@126.com
//参考P166
//Code::Block vs
#include <iostream>
using   namespace   std;class Base
{private:char* m_buf;size_t m_nsize;int* m_count;public://constructorBase(size_t n = 1){m_buf = new char[n];m_count = new int;m_nsize = n;*m_count = 1;cout << "constructor----count is: " << *m_count << endl;}//copy constructorBase(const Base& bobj){m_nsize = bobj.m_nsize;m_buf = bobj.m_buf;m_count = bobj.m_count;(*m_count)++;cout << "copyconstructor----count is: " << *m_count << endl;}//operator=Base& operator=(const Base& bobj){if (m_buf == bobj.m_buf){return *this;}delete this;m_buf = bobj.m_buf;m_nsize = bobj.m_nsize;m_count = bobj.m_count;(*m_count)++;cout << "count is: " << *m_count << endl;return *this;}//destructor~Base(){(*m_count)--;cout << "count is:" << *m_count << endl;if (*m_count == 0){cout << "destructor" << endl;delete[] m_buf;delete m_count;}}char* getbuf(){return m_buf;}
};int main()
{Base obj1(100);Base obj2(200);//测试copy constructorBase obj3 = obj1;//测试operator=obj2 = obj1;cout << "-------Test-------" << endl;strcpy(obj1.getbuf(), "cs_wuyg");cout << obj2.getbuf() << endl;cout << obj3.getbuf() << endl;system("pause");return 0;
}
/*
constructor----count is: 1
constructor----count is: 1
copyconstructor----count is: 2
count is:0
destructor
count is: 3
-------Test-------
cs_wuyg
cs_wuyg
请按任意键继续. . .
count is:2
count is:1
count is:0
destructor
*/

5、智能指针std::auto_ptr

//智能指针auto_ptr.cpp
//2010.8.28
//coder:cs_wuyg@126.com
//参考P170
//Code::Blocks 10.05 VS2005/2008
#include <iostream>
#include <string>
#include <memory>//使用auto_ptr
using   namespace   std;class Base
{public:Base(size_t m_nSize = 10) : m_nSize(m_nSize){cout << "constructor" << endl;}~Base(){cout << "destructor" << endl;}void foo(){cout << "-----foo()-----" << endl;cout << "m_nSize is : " << m_nSize << endl;}private:size_t m_nSize;
};int main()
{//定义指向string的智能指针auto_ptr<string> pString(new string("hello, auto_ptr"));cout << *pString << endl;//定义指向Base类对象的智能指针auto_ptr<Base> pBase(new Base(100));pBase->foo();system("pause");return 0;
}
/*
测试:
hello, auto_ptr
constructor
-----foo()-----
m_nSize is : 100
请按任意键继续. . .
destructor
*/

6、作用域智能指针boost::scoped_ptr

//scoped_ptr.cpp
//2010.8.29
//coder:cs_wuyg@126.com
//参考P173
#include <iostream>
#include <boost\scoped_ptr.hpp>
using   namespace   std;class Base
{public:Base(size_t m_nSize = 10) : m_nSize(m_nSize){cout << "constructor" << endl;}~Base(){cout << "destructor" << endl;}void foo(){cout << "-----foo()-----" << endl;cout << "m_nSize is : " << m_nSize << endl;}private:size_t m_nSize;
};int main()
{boost::scoped_ptr<Base> pBase(new Base);system("pause");return 0;
}
/*
constructor
请按任意键继续. . .
destructor
*/

7、boost::scoped_array

//scoped_array.cpp
//coder:cs_wuyg@126.com
//2010.8.29
//参考P173
//测试结果表明,auto_ptr用于数组会有内存泄露
#include <iostream>
#include <memory>
#include <boost/scoped_array.hpp>
using   namespace   std;class Base
{public:Base(size_t m_nSize = 10) : m_nSize(m_nSize){cout << "constructor" << endl;}~Base(){cout << "destructor" << endl;}void foo(){cout << "-----foo()-----" << endl;cout << "m_nSize is : " << m_nSize << endl;}private:size_t m_nSize;
};void boostarray()
{boost::scoped_array<Base> pBase(new Base[2]);
}void autoarray()
{auto_ptr<Base> pBase2(new Base[2]);
}
int main()
{cout << "--------boost array--------" << endl;boostarray();cout << "--------boost array--------" << endl;cout << "--------auto array--------" << endl;autoarray();cout << "--------auto array--------" << endl;system("pause");return 0;
}
/*
--------boost array--------
constructor
constructor
destructor
destructor
--------boost array--------
--------auto array--------
constructor
constructor
destructor
--------auto array--------
*/

8、boost::shared_ptr

//share_ptr.cpp
//2010.8.29
//coder:cs_wuyg@126.com
//共享智能指针
//参考P174
//Code::Blocks  vs2005/2008
#include <iostream>
#include <string>
#include <boost/shared_ptr.hpp>
using   namespace   std;class Base
{public:Base(size_t m_nSize = 10) : m_nSize(m_nSize){cout << "constructor" << endl;}~Base(){cout << "destructor" << endl;}void foo(){cout << "-----foo()-----" << endl;cout << "m_nSize is : " << m_nSize << endl;++m_nSize;}private:size_t m_nSize;
};void  shareptr()
{//试试Base对象使用智能指针cout << "------------------" << endl;boost::shared_ptr<Base> pBase1(new Base);boost::shared_ptr<Base> pBase2(pBase1);pBase1->foo();pBase2->foo();cout << "------------------" << endl;//测试使用use_count、resetcout << "use count is : " << pBase2.use_count() << endl;pBase1.reset();cout << "use count is : " << pBase2.use_count() << endl;pBase2.reset();//试试string对象使用智能指针cout << "------------------" << endl;boost::shared_ptr<string> pstr1(new string("cswuyg"));boost::shared_ptr<string> pstr2(pstr1);cout << pstr1 << endl;cout << pstr2 << endl;
}int main()
{shareptr();system("pause");return 0;
}
/*
------------------
constructor
-----foo()-----
m_nSize is : 10
-----foo()-----
m_nSize is : 11
------------------
use count is : 2
use count is : 1
destructor
------------------
003844A0
003844A0
请按任意键继续. . .
*/

9、boost::shared_array

//共享数组指针.cpp
//2010.8.29
//coder:cs_wuyg@126.com
//练习共享数组智能指针。boost::shared_array
//Code::Blocks vs2005/2008
#include <iostream>
#include <string>
#include <boost/shared_array.hpp>
using   namespace   std;class Base
{public:Base(size_t m_nSize = 10) : m_nSize(m_nSize){cout << "constructor" << endl;}~Base(){cout << "destructor" << endl;}void foo(){cout << "-----foo()-----" << endl;cout << "m_nSize is : " << m_nSize << endl;}private:size_t m_nSize;
};int main()
{boost::shared_array<Base> pstr1(new Base[2]);boost::shared_array<Base> pstr2(pstr1);cout << &pstr2[0] << endl;cout << &pstr2[1] << endl;cout << "----------------" << endl;cout << &pstr1[0] << endl;cout << &pstr1[1] << endl;system("pause");return 0;
}
/*
constructor
constructor
00382E74
00382E78
----------------
00382E74
00382E78
请按任意键继续. . .
destructor
destructor
*/

10、boost::weak_ptr

//weak_ptr.cpp
//coder:cs_wuyg@126.com
//2010.8.29
//参考P179
//Code::Blocks  vs2005/2008
#include <iostream>
#include <memory>
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>
using   namespace   std;class Base
{public:Base(size_t m_nSize = 10) : m_nSize(m_nSize){cout << "constructor" << endl;}~Base(){cout << "destructor" << endl;}void foo(){cout << "-----foo()-----" << endl;cout << "m_nSize is : " << m_nSize << endl;++m_nSize;}private:size_t m_nSize;
};void testptr()
{boost::shared_ptr<Base> pBase1(new Base);boost::weak_ptr<Base> pBase2(pBase1);// pBase1.reset();//加上这句作测试,weak_ptr的作用,使用weak_ptr的lock可以保证shared_ptr对象存在时才执行。boost::shard_ptr<Base> pBase2_1(pBase2->lock());// 提升为shard_ptr,线程安全.保证下面执行时的执行引用计数至少为1.if (pBase2_1){pBase2_1->foo();}else{cout << "Base obj is delete!!" << endl;}try{boost::shared_ptr<Base> pBase3(pBase2);pBase3->foo();}catch(boost::bad_weak_ptr){cout << "Base obj is delete, can not construct shared_ptr!!" << endl;}
}int main()
{testptr();system("pause");return 0;
}
/*
constructor
-----foo()-----
m_nSize is : 10
-----foo()-----
m_nSize is : 11
destructor
请按任意键继续. . .
*/

View Code

《C++应用程序性能优化::第五章动态内存管理》学习和理解相关推荐

  1. Java 程序性能优化《第一章》Java性能调优概述 1.4小结

    Java 程序性能优化<第一章>1.4小结 通过本章的学习,读者应该了解性能的基本概念及其常用的参考指标.此外,本章还较为详细的介绍了与性能调优相关的两个重要理论--木桶原理以及Amdah ...

  2. 精通安卓性能优化-第五章(三)

    Concurrency 在java.util.concurrent.atomic和java.util.concurrent.locks包中定义了更多的类.java.util.concurrent.at ...

  3. java substring 性能_《Java程序性能优化》subString()方法的内存泄露

    String的构造 首先了解下String的构造,String内部使用char [] value 来存储字符. 需要注意 offset和count在1.7已经没有了. /** The value is ...

  4. C++深入浅出(五)—— 动态内存管理

    文章目录 前言 1. C/C++ 内存分布 2. C语言内存管理方式 3. C++内存管理方式

  5. C++应用程序性能优化

    C++应用程序性能优化 C++应用程序性能优化系列博客主要依据<C++应用程序性能优化>(第2版)学习整理而来,同时参考部分网络博客. C++应用程序性能优化(一)--应用程序性能优化简介 ...

  6. C++应用程序性能优化读书笔记

    粗浅的看了下,有点虎头蛇尾.暂且记下. 第一篇 C++程序优化基础 第1章 C++对象模型 1.1 基本概念 1.1.1 程序使用内存区 一个程序占用的内存区一般分为5种:全局/静态数据区.常量区.代 ...

  7. C++ 应用程序性能优化,第 6 章:内存池

    引言 本书主要针对的是 C++ 程序的性能优化,深入介绍 C++ 程序性能优化的方法和实例.全书由 4 个篇组成,第 1 篇介绍 C++ 语言的对象模型,该篇是优化 C++ 程序的基础:第 2 篇主要 ...

  8. 《C++应用程序性能优化::第二章C++语言特性的性能分析》学习和理解

    <C++应用程序性能优化::第二章C++语言特性的性能分析>学习和理解 说明:<C++应用程序性能优化> 作者:冯宏华等 2007年版.最近出了新版,看了目录,在前面增加了一章 ...

  9. asp.net程序性能优化的七个方面

    asp.net程序性能优化的七个方面 一.数据库操作 1.用完马上关闭数据库连接 访问数据库资源需要创建连接.打开连接和关闭连接几个操作.这些过程需要多次与数据库交换信息以通过身份验证,比较耗费服务器 ...

最新文章

  1. Ubuntu21.04开机自动挂载Windows硬盘分区
  2. java shp求相交面积_shp文件自相交处理的方法
  3. 小米手机60帧录屏_手机录屏怎样只录手机内部声音不录入外部声音?教你三种方法,一定能帮到你...
  4. 微服务升级优点_微服务–——定义, 原则 和 优点
  5. java定时器 并发_【java多线程与并发库】— 定时器的应用 | 学步园
  6. windows下Emacs的安装与配置
  7. zookeeper分布式锁原理及实现
  8. 【bzoj4753】[Jsoi2016]最佳团体 分数规划+树形背包dp
  9. 第 1 章 FreeBSD Install
  10. ColorUI使用与技巧
  11. unity3d如何利用asset store下载一些有用的资源包
  12. 自来水供水收费管理系统
  13. 首都师范 博弈论 5 3 1合作博弈与数学表达
  14. 自然常数e的由来(简单通俗易于理解自然常数e)
  15. 找出java重复字符串,java 找出字符串出现重复的字符和次数
  16. mybatis-基本架构
  17. 20级逍遥装备材料汇总及出处
  18. 公开课笔记 | 从0到1搭建数据运营体系
  19. java反编译工具gd gson,浅谈Android中static修饰符,及Gson转String实例
  20. 手眼标定_全面细致的推导过程

热门文章

  1. java连接mysql数据库增删改查_java连接mysql数据库增删改查操作记录
  2. 什么是Hystrix
  3. reg类型变量综合电路_verilog中reg和wire类型的区别
  4. 高品质摄影作图台式计算机推荐,浅谈高质量摄影照片后期输出的打印机选择
  5. 中如何构造有参和无惨_CAD制图初学入门:CAD机械软件中如何构造孔?
  6. 【RocketMQ工作原理】offset管理
  7. Redis最佳实践:业务层面和运维层面优化
  8. android菜单(menu)资源
  9. mac终端python不能显示中文_Matplotlib为Mac显示中文,ForMac
  10. ie浏览器修复工具_电脑故障修复不求人!50个小工具可帮你一键修复