《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:
//constructor
Base(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 constructor
Base(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 constructor
Base 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、reset
cout << "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 请按任意键继续. . . */
浙公网安备 33010602011771号