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


    《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
  • 相关阅读:
    CH02 FPGA设计Verilog基础笔记(二)
    同一个按键短按与长按的区别触发
    树莓派 -- 输入设备驱动 (key) 续2: 转载 Setting up a GPIO-Button “keyboard” on a Raspberry Pi
    树莓派 -- 输入设备驱动 (key) 续1
    树莓派 -- 输入设备驱动 (key)
    树莓派 -- 按键 (key)使用BCM2835 gpio library
    leds-gpio driver 续1
    leds-gpio driver
    使用CSDN-markdown编辑器
    树莓派
  • 原文地址:https://www.cnblogs.com/cswuyg/p/1812162.html
Copyright © 2020-2023  润新知