IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> 49~52new和delete -> 正文阅读

[开发工具]49~52new和delete


一、了解new-handler的行为

  • set_new_handler
namespace std{
    typedef void (*new_handler)();
    new_handler set_new_handler(new_handler p) throw();//trhow()是一份异常明细,表示该函数不抛出任何异常
}

set_new_handlerd的参数是个指针,指向operator new无法分配足够内存时该被调用的函数。其返回值也是个指针,指向set_new_handler被调用前正在执行的那个new-handler函数。
当set_new_handler设置的函数中有分配内存的行为,若operator new 无法满足内存申请时,则会一直调用set_new_handler设置的函数,直到申请内存成功。

  • 设计良好的new-handler应该做到哪些?
    • 让更多的内存可被使用
      让更多的内存可被使用,会使operator new 下一次内存申请成功。实现该策略的一个方法是:程序一开始就分配一大块内存,当new-handle第一次被调用时,将它们还给程序使用。
    • 安装另一个new-handelr
      目前的new-handelr无法获取更多的可用内存,安装另一个有此能力的new-handelr。或者让当前的new-handelr修改自己的行为,即修改static数据,namespace数据,或global数据。
    • 卸除new-handler
      将null传给set_new_handler。没有安装handler,则operater new 会在内存分配不成功时抛出异常。
    • 抛出bad_alloc(或派生自bad_alloc)的异常 这样的异常不会被operator new 捕捉,因此会被传播到内存索求处。
    • 不返回 通常调用abort或exit。
  • 如何以不同的方式处理内存分配失败的情况?
    定义专属的operator new,如下:
class NewHandlerHolder{
public:
    explicit NewHandlerHolder(std::new_handle nh)
    : handler(nh){}
    ~NewHandlerHolder()
    {
        std::set_new_handler(handler);
    }
private:
    std::new_handler handler;
    NewHandlerHolder(const NewHandlerHolder&);
    NewHandlerHolder& operator=(const NewHandlerHolder&);
};

class Widget{
public: 
    static std::new_handler set_new_handler(std::new_handler p) throw();
    static void* operator new(std::size_t size) throw(bad_alloc);
private:
    static std::new_handler currentHandler;
};
std::new_handler Widget::currentHandler = 0;

std::new_handler Widget::set_new_handler(std::new_handler p) throw(){
    std::new_handler oldHandler = currentHandler;
    currentHandler = p;
    return oldHandler;
}

void* operator Widget::new(std::size_t size) throw(bad_alloc){
    NewHandlerHolder h(std::set_new_handler(currentHandler));
    return ::operator new(size); //指明调用全局的new,否则会调用Widget::new
}
  • new操作符的查找规则:当new一个类型时,会现在该类型的作用域内查找new操作符,没有则在该类型的上一级作用域,直至全局作用域内的new——即标准的new。
  • class专属之set_new_handler
template<typename T>
class NewHandlerSupport{
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;
};

template<typename T>
std::new_handler 
NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw(){
    std::new_handler oldHandler = currentHandler;
    currentHandler = p;
    return oldHandler;
}

template<typename T>
void* NewHandlerSupport<T>::operator Widget::new(std::size_t size) throw(std::bad_alloc){
    NewHandlerHolder h(std::set_new_handler(currentHandler));
    return ::operator new(size); //指明调用全局的new,否则会调用Widget::new
}

template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;

class Widget:public NewHandlerSupport<Widget>{
    ...
}

注意到NewHandlerSupport模板从未使用到模板参数T。将NewHandlerSupport定义为模板,实际是希望继承自NewHandlerSupport的每一个class类型都有一个独立的static成员变量currentHandler,因为最初就是希望每种类型都有自己的专属new_handler和operator new。T只是用来区分不同的derived classes。

  • nothrow.
    • nothrow 是C++委员会为兼容之前new返回null的版本而设计的。
    • nothrow的缺陷:new (std::nothrow) Widget意味着new不抛异常,但是new申请内存成功后,执行构造函数可能会抛出异常,因为构造函数中可能使用的是抛出异常的new,所以nothrow的用处不大。
Widget* pw2 = new (std::nothrow) Widget; //不抛异常,如内存申请失败则返回nul

二、了解new和delete的合理替换时机

  • 用来检测运行上的错误
    • new所得内存delete掉却不幸失败会导致内存泄露
    • 在new所得内存身上多次delete则会导致不确定行为
    • 各式各样的错误会导致数据“overruns”(写入点在分配区块的尾端之后)或“underruns”(写入点在分配区块的起点之前)
      自定义operator new,便可以超额分配内存,以额外空间(位于客户所的区块之前或后)放置特定的bytw patterns(即签名,signatures)。operator deletes便可以检查上述签名是否原封不动,若否就表示在分配区的某个生命时间点发生了overrun或underrun,这时候operator delete记录那个事实以及惹事生非的指针。
  • 为了强化效能
    编译器所带的new和delete主要用于一般目的:既可以被长时间执行的程序接受,也可以被少于1秒的程序接受,还要满足处理大块内存、小块内存、大小混合型内存等等,还要考虑破碎内存。因此效率上比较中庸,定制的new和delete性能胜过缺省版本,节省内存,最高可省50%。
  • 为了收集使用上的统计数据
    定制new和delete可以收集到的信息:
    • 分配区块大小
    • 倾向于以FIFO或LIFO(后进先出)次序或随机次序来分配和归还
    • 在不同的执行阶段有不同的分配/归还形态么?
    • 任何时刻所使用的最大动态分配量的最高水位是多少

三、编写new和delete时需固守常规

  • C++规定,即使客户要求0 bytes,operator new 也得返回一个合法的指针
  • new 中包含着一个无限循环。如果new-handler不是设计良好的(如第一节所属),则该循环永远不会退出。
  • 当operator new被继承时,Derived classed可能会采用base classes中的operator new,应为operator new只是针对Base classes的,应用到Derived classes会产生申请错误——Derived classes对象的内存更大,所以Base classes 中处理这种情况的最佳做法是:将派生类的创建交由::opertor new 处理。
  • opertor delete应该在收到null指针时不做任何事。class 专属版本的delete同样需要处理上述Drived classes和Base classed 间的关系。
  • 一般不需要重写new 和delete,需要考虑内存对齐等比较复杂的问题。有商业版本即开源的定制new和delete,如,boost库中提供的new和delete更适合分配大量小型对象,

四、写了placement new 也要写placement delete

  • placement new (带额外参数的new) 标准版本的operator new 中接受的参数除了有std::size_t之外还有其他参数。placement delete的定义同理。
    void* operator new(std::size_t size) throw(std::bad_alloc);                //标准版本
    void* operator new(std::size_t size, void* pMemory) throw(); //众多palcement版本中最有用的一个
  • new时会进行两件事:1)申请内存;2)调用构造。若1)成功,2)失败,则需要运行期系统来释放内存,因为此时申请的内存还没有交到客户手上。
  • 提供placement new,也许提供placement delete,否则运行期系统无法找到合适版本的delete,将导致内存泄漏。
  • 提供了placement delete,若没有发生异常,delete 时调用的是标准版本的delete。placement delete 只有在“伴随placement new调用而出发的构造函数”出现异常时才会被调用。
  • 避免class 专属的news 掩盖其外围作用域中的其他news(包括标准版本的new)。
  • C++ 在global作用域中提供以下形式的operator new:
    void* operator new(std::size_t) throw(std::bad_alloc); 
    void* operator new(std::size_t, void* ) throw();
    void* operator new(std::size_t,const std::nothrow&) throw();
  • 如何避免遮掩new 和 delete
//按如下方式可避免遮掩
class StandardNewDeleteForms{
public:
    static void* operator new(std::size_t size) throw(std::bad_alloc){
       return ::operator new(size);    }
    static void* operator delete(void* pMemory) throw(){
       return ::delete(pMemory);     }
       
    static void* operator new(std::size_t size, void* ptr) throw(){
       return ::operator new(size, ptr);  }
    static void* operator delete(void* pMemory,void* ptr) throw(){
       return ::delete(pMemory, ptr);  }

    static void* operator new(std::size_t size, const std::nothrow& nt ) throw(){
     return ::operator new(size, ptr);}
    static void* operator delete(void* pMemory,const std::nothrow& nt) throw(){    
     return ::delete(pMemory, nt);}
};

class Widget : public StandardNewDeleteForms{
public:
    using StandardNewDeleteForms::operator new;
    using StandardNewDeleteForms::operator delete;
};
  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2022-05-26 15:22:48  更:2022-05-26 15:23:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 12:25:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码