effective c++学习笔记六

 

定制new和delete

 

STL的内存不由new和delete管理,是由allocator管理
 
 
 
条款49:了解new-handler的行为
 
当new不能满足分配空间的要求时,会抛出异常,在这之前会先调用一个客户指定的错误处理函数,一个所谓的new-handler。为了指定这个用以处理内存不足的函数,客户必须调用set_new_handler
使用举例:
#include<iostream>
using namespace std;

namespace std{
    typedef void (*new_handler)();
    new_handler set_new_handler(new_handler p) throw();
}

void outofmen()
{
    std::cerr<<"out of memory"<<endl;
    std::abort();
}

int main()
{
    std::set_new_handler(outofmen);
    int *testBigData = new int [1000000000L];
}

 

 当new无法满足分配内存的要求时,会不断调用new-handler函数,直到找到足够的内存,一个设计良好的new handler必须做以下事情:
1.让更多的内存可以被使用:将会使下一次内存分配动作可能成功。一般做法是,一开始分配一大块内存,在new-handler第一次被调用时将他们还给程序使用。
2.安装另一个new-handler,下一次调用时会调用新的函数
3.卸载new-handler 也就是将set_new_handler设置为null,不会产生异常
4.抛出bad_alloc异常,这样的异常不会被new捕捉,因此可以传播到内存所求处
5.不返回,直接abort或者exit
 
 
实现一个class专属的new_handler:
class Widget{
    public:
    static std::new_handler set_new_handler(std::new_handler p)throw();
    static void* operator new(std::size_t size) throw(std::bad_alloc);
    private:
    static std::new_handler currentHandler;
};
std::new_handler Widget::currentHandler = 0;
//set_new_handler 函数会将它获得的指针存储起来,然后返回先前存储的指针 
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
    std::new_handler oldHandler = currentHandler;
    currentHandler = p;
    return oldHandler;
}

 

最后,widget的new做以下事情
1.调用标准set_new_handler,告知widget的错误处理函数。这会将类的new_handler变成global new_handler
2.调用global new 执行内存分配,如果失败,则会调用class的new_handler,那个函数刚刚安装为global new_handler,如果分配失败,则将抛出一个bad_alloc异常,此时类的new必须恢复为原本global的new_handler,然后再传播该异常。
3.如果global new可以分配内存,类的new会返回一个指针,指向分配所得,类的析构函数会挂历global new_handler,他会将类的new被调用前的那个global new_handler恢复过来。
 
 
表达式new(std::nothrow)Widget发生两件事:
nothrow版本的operator new被调用,用以分配足够的内存,如果失败返回null,如果成功则调用widget的构造函数,构造函数可能会抛出异常,因此nothrow new只能保证operator new不抛出异常,但是不能保证整个表达式无异常,因此没有用这个表达式的必要。
 
 
 
 
条款50:了解new和delete的合理替换时机
 
替换new和delete的理由
1.用来检测运用上的错误
2.强化效能
3.收集使用上的统计数据
4.为了增加分配和归还的速度
5.为了降低缺省内存管理器带来的空间额外开销
6.弥补缺省分配器中的非最佳对齐
7.为了将相关对象成簇集中
8.获得非传统行为
 
对齐:很多计算机体系都要求特定类型必须放置在特定的内存地址上。如果不能做到,就会导致运行期异常。
因此分配大量小型对象会导致一些内存的浪费,Boost程序库的Pool是一个内存管理器,他对常见的分配大量对象很有帮助。TR1支持各类特定的对齐条件。
 
 
 
 
条款51:编写new和delete时需要固守常规
 
new 必须返回正确的值,内存不足时必须调用new_handler函数,必须有对付零内存的要求,还需要避免掩盖正常形式的new
当内存不足时,new不断调用new_handler,除非new_handler指向null,new才会抛出异常
即使客户要求0byte,new也需要返回一个合法的地址。
 
void* operator new(std::size_t size) throw(std::bad_alloc)
{
    using namespace std;
    if(size==0)
    {
        size=1;
    }
    while(true)
    {
        //alloc memory
        if(alloc success)
        {
            return point to the memory;
        }
        
        //alloc fail
        new_handler globalHandler (*globalHandler) ();
                //只能用在这种方式来取得函数指针
        set_new_handler(globalHandler);
        
        if(globalHandler) (*globalHandler) ();
        else throw std::bad_alloc();
    }
}

 

 
delete的情况很简单,唯一需要注意的事情是保证删除null指针永远安全
void base::operator delete(void *rawMemory, std::size_t size) throw()
{
    if(rawMemory == 0) return;
    if(size != sizeof(base))
    {
        ::operator delete(rawMemory);
        return;
    }
    //return the rawMemory
    return;
}

 

 
 
条款52:写了placement new也需要写placement delete
 
当调用 widget *pt = new widget; 时,共有两个函数被调用,一个用以分配内存,另一个为widge的构造函数
假设第一个调用成功而第二个失败,系统有责任取消已经分配的内存并且恢复状态。如果运行期系统不知道真正调用的是哪个new就无法恢复。运行期系统寻找“参数个数和类型都和new相同的delete”,如果找到,就是他调用的对象。
必须同时提供一个正常版本的delete和一个placement delete,后者参数和placement new一样。
但是因为名称覆盖,当心避免让专属new覆盖正常形式的new delete同理
 
 
 
条款53:不要轻易忽略编译器的警告
 
 
条款54:让自己熟悉包括TR1在内的标准程序库
 
TR1有14个新组建,统统都放在std名称空间下
例如 
1.智能指针 
tr1::shared_ptr和tr1::weak_ptr 引用计数 自动销毁
2.tr1::function 
可以表示任何可调用物, 任何函数或者函数对象
3.tr1::bind
能做stl绑定期bind1st和bind2nd所能做的每一件事,tr1::bind可以喝const和non-const函数协同运作,也可以和by-reference参数协作
 
还有很多其他的 用到了再看
TR1只是一份规范,为了获得他的好处,还需要一份实物,一个好的实物来源是Boost
 
 
条款55:让自己熟悉Boost
 
 
posted @ 2012-09-11 10:55  w0w0  阅读(165)  评论(0编辑  收藏  举报