《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
请按任意键继续. . .
*/
View Code

 

posted on 2010-08-30 00:34  烛秋  阅读(1867)  评论(0编辑  收藏  举报