C++的new和delete使用示例详解

目录
  • 1. new 和 delete
    • 三种 new
  • 2. operator new 和 operator delete
  • 3. 重载 operator new 和 operator delete 有什么用啊? 有用?
    • 使用 内存池
    • 不使用内存池
    • 对比结果

1. new 和 delete

C++中,动态的分配对象和释放对象我们使用的是newdelete那么,newdeletec 语言中的 mallocfree有什么区别呢?

我们为什么又要使用newdelete 呢?

首先 在C++中,把 newdelete 改成了关键字;

new主要做三件事:调用operator new分配空间、初始化对象、返回指针。
这个时候,就体现出newmalloc 的区别来了,初始化对象,且返回的是一个该类的对象指针。

malloc 只是单纯的分配空间, 其他的事,一律不管。
若要用malloc体现出new的价值,大抵可以这样写一下(我自己的拙见)

template<typename T, typename ...PACK>
T *NEW(PACK... pack) {
  T *tmp = (T *) malloc(sizeof(T)); // 调用 malloc分配指针
  tmp->T::init(pack...); // 初始化对象
  return tmp; // 返回指针
}

示例类

struct E {
  int x, y;
  void init() {this->x = 0;this->y = 0;}
  void init(int x) {
    init();
    this->x = x;
  }
  void init(int x, int y) {
    init();
    this->x = x;this->y = y;
  }
  void print() {cout << x << " " << y << endl;}
  void add(const E &rsh) {
    this->x += rsh.x;
    this->y += rsh.y;
  }
};

使用

auto tmp1 = NEW<E>(1);
auto tmp2 = NEW<E>(1, 2);
tmp1->print();
tmp2->print();

不够像的地方: 因为类调用不了自身的构造函数,构造函数的隐式调用的。 所有我采用了一个init函数来充当我的伪构造函数。(反正分配内存时也没调用构造函数嘛)

三种 new

抛异常的 new

void *operator new(std::size_t count)
throw(std::bad_alloc);
try {
  const long long N = 10e18;
  auto p = new int8_t[N];
  delete p;
} catch (std::bad_alloc &bad) {
  cout << bad.what() << endl;
}

不抛异常的 new

void *operator new(std::size_t count,
   const std::nothrow_t&) throw();
long long N = 10e18;
auto p = new(std::nothrow) int8_t[N];
assert(p);

palcement new在已有的内存上构建,不重新分配内存

struct E {
  int x, y;
};
auto mall = ::malloc(100);
auto p = new(mall) E{.x = 1, .y = 2};
int32_t *now = static_cast<int32_t *>(mall);
cout << *now << endl;
cout << *++now << endl;
free(mall);

对于第三种,很有意思,相当于把 分配内存和构造分开的设计

是不是有点眼熟?

啊对对对

就是alloctor的搞法嘛。

有时候构造操作是一个比较耗时的操作,这个时候就有用了

struct E {
  int x, y;
  E(int x = 0, int y = 0) : x(x), y(y) {}
  void print() { cout << x << " " << y << endl; }
  void add(const E &rsh) {
    this->x += rsh.x;
    this->y += rsh.y;
  }
};
template<typename T>
T *allocate() { return static_cast<T *>(::malloc(sizeof(T))); }
template<typename T, typename ...PACK>
T *construct(void *ptr, PACK... pack) {
  return new(ptr) T(pack...);
}
signed main() {
  auto t = allocate<E>();
  t = construct<E>(t, 1, 2);
  t->print();
  return 0;
}

2. operator new 和 operator delete

operator new 就是一个运算符了,且我们可以重载这个运算符,使其分配内存的方式是我们自定义的分配方式。

比如重载 operator new 来给 new

struct E {
  E() { cout << "construct" << endl; }
  void *operator new(std::size_t length) {
    cout << "malloc object" << endl;
    return ::malloc(length);
  }
  void *operator new[](std::size_t length) {
    cout << "malloc array" << endl;
    return ::malloc(length);
  }
  void operator delete(void *now) {
    cout << "free object" << endl;
    ::free(now);
  }
  void operator delete[](void *now) {
    cout << "free array" << endl;
    ::free(now);
  }
};
signed main() {
  auto t = new E;
  auto tarray = new E[2];
  delete t;
  delete[] tarray;
}
/* output
malloc object
construct
malloc array
construct
construct
free object
free array
*/

3. 重载 operator new 和 operator delete 有什么用啊? 有用?

当然,是有用的。在某些时候,可能重载分配方式可能是一个比较好的选择

众所周知,若是频繁的分配一些小对象又释放的,这样子的操作是不好的。

虽然操作系统可以帮我们干一些脏活累活,可是,若是根据性能瓶颈分析,发现问题出在分配小对象上面,这个时候我们就可以考虑整个内存池了。(内存分配策略是个大活,我这里的代码只是简单示例)

使用 内存池

struct E {
  int arr[100];
  void *operator new(std::size_t size);
  void operator delete(void *ptr);
};
struct ENode {
  void *node;
  ENode *next;
};
const int MAX_LENGTH = 1024;
ENode *poll = nullptr, *use = nullptr;
auto init = []() -> bool {
  auto now = poll;
  for (int i = 0; i < MAX_LENGTH; i++) {
    if (i == 0) poll = now = new ENode{.node = malloc(sizeof(E)), .next = nullptr};
    else {
      auto pre = now;
      now = new ENode{.node = malloc(sizeof(E)), .next = nullptr};
      pre->next = now;
    }
  }
  return true;
}();
void *E::operator new(std::size_t size) {
  assert(poll != nullptr);
  auto now = poll;
  poll = poll->next;
  now->next = use;
  use = now;
  return now->node;
}
void E::operator delete(void *ptr) {
  assert(use != nullptr);
  auto now = use;
  use = use->next;
  now->next = poll;
  poll = now;
  poll->node = ptr;
}
auto t = std::chrono::high_resolution_clock::now();
E *arr[1024];
for (int i = 0; i < 1000; i++) {
  for (int j = 0; j < 1024; j++) {
    arr[j] = new E;
  }
  for (int j = 0; j < 1024; j++) {
    delete arr[(j + i) % 1024];
  }
}
cout << (std::chrono::high_resolution_clock::now() - t).count() << endl;

不使用内存池

struct W {
  int arr[100];
};
auto t = std::chrono::high_resolution_clock::now();
W *arr[1024];
for (int i = 0; i < 1000; i++) {
  for (int j = 0; j < 1024; j++) {
    arr[j] = new W;
  }
  for (int j = 0; j < 1024; j++) {
    delete arr[(j + i) % 1024];
  }
}
cout << (std::chrono::high_resolution_clock::now() - t).count() << endl;

对比结果

在上述测试中, 使用内存池的时间稳定在 7.5 ms左右

不使用内存池稳定在 108 ms左右

当然,我这个测试是片面的,在这里,我只是想说明,重载 operator newoperator delete 的用途与好处

以上就是C++的new和delete使用示例详解的详细内容,更多关于C++ new和delete使用的资料请关注我们其它相关文章!

(0)

相关推荐

  • C++ 动态内存分配详解(new/new[]和delete/delete[])

    一.为什么需要动态内存分配? 在C++程序中,所有内存需求都是在程序执行之前通过定义所需的变量来确定的. 但是可能存在程序的内存需求只能在运行时确定的情况. 例如,当需要的内存取决于用户输入. 在这些情况下,程序需要动态分配内存,C ++语言将运算符new和delete合成在一起. (1)特点 1.C++中通过new关键字进行动态内存申请 2.C++中的动态内存分配是基于类型进行的 3.delete关键字用于内存释放 (2)语法 ①变量申请: Type* pointer = new Type;

  • 最新C/C++中的new和delete的实现过程小结

    目录 new delete new[] delete[] 下面是<C++ Primer 5th>中P726 对 new 和 delete 过程的解释: 当我们使用一条new表达式时,实际上执行了三步操作: new表达式调用一个名为 operator new (或者 operator new[] )的标准库函数.该函数分配一块足够大的.原始的.未命名的内存空间以便存储特定类型的对象(或者对象数组). 编译器运行相应的构造函数以构造这些对象,并为其传入初始值. 对象被分配了空间并构造完成,返回一个

  • 深入理解C++中的new和delete并实现对象池

    深入理解new和delete new和delete称作运算符 我们转反汇编看看 这2个运算符本质也是相应的运算符的重载的调用 malloc和new的区别? 1.malloc按字节开辟内存的:new开辟内存时需要指定类型 new int[10] 所以malloc开辟内存返回的都是void* 而new相当于运算符的重载函数 operator new ->返回值自动转成指定的类指针 int* 2.malloc只负责开辟空间,new不仅仅有malloc的功能,可以进行数据的初始化 new int(20)

  • C++ 使用new与delete需注意的原则

    C++的动态内存管理是通过new和delete两个操作来完成的,即用new来申请空间,用delete来释放空间.在使用new和delete时,注意以下原则. 1.new与delete需一一对应 用new操作申请空间,如果申请成功,必须在以后的某个时刻用delete释放该空间,既不能忘记释放,也不能多次释放.前者会引起内存泄露,后者会引起运行时错误.如下面的程序. #include <iostream> using namespace std; int main() { int *p; p=ne

  • 深入理解C++中的new/delete和malloc/free动态内存管理及区别介绍

    malloc/free和new/delete的区别 malloc/free是C/C++标准库的函数:new/delete是C++操作符. malloc/free只是动态分配内存空间/释放空间:new/delete除了分配空间还会调用构造函数和析构函数进行初始化与清理资源. malloc/free需要手动计算类型大小且返回值类型为void*:new/delete可自动计算类型的大小,返回对应类型的指针. malloc/free管理内存失败会返回0:new/delete等的方式管理内存失败会抛出异常

  • C++的new和delete详解

    目录 1.new和delete的内部实现 2.placement技术 3.new和delete运算符重载 4.对象的自动删除技术 1.new和delete的内部实现 C++中如果要在堆内存中创建和销毁对象需要借助关键字new和delete来完成.比如下面的代码 class CA { public: CA()m_a(0){} CA(int a):m_a(a){} virtual void foo(){ cout<<m_a<<endl;} int m_a; }; void main()

  • C++的new和delete使用示例详解

    目录 1. new 和 delete 三种 new 2. operator new 和 operator delete 3. 重载 operator new 和 operator delete 有什么用啊? 有用? 使用 内存池 不使用内存池 对比结果 1. new 和 delete 在C++中,动态的分配对象和释放对象我们使用的是new 和 delete那么,new 和 delete 与 c 语言中的 malloc 和 free有什么区别呢? 我们为什么又要使用new 和 delete 呢?

  • php示例详解Constructor Prototype Pattern 原型模式

    原型模式中主要角色 抽象原型(Prototype)角色:声明一个克隆自己的接口 具体原型(Concrete Prototype)角色:实现一个克隆自己的操作 当一个类大部分都是相同的只有部分是不同的时候,如果需要大量这个类的对象,每次都重复实例化那些相同的部分是开销很大的,而如果clone之前建立对象的那些相同的部分,就可以节约开销. 针对php的一种实现方式就是__construct()和initialize函数分开分别处理这个类的初始化,construct里面放prototype也就是公共的

  • Vue中的vue-resource示例详解

    vue-resource特点 vue-resource插件具有以下特点: 1. 体积小 vue-resource非常小巧,在压缩以后只有大约12KB,服务端启用gzip压缩后只有4.5KB大小,这远比jQuery的体积要小得多. 2. 支持主流的浏览器 和Vue.js一样,vue-resource除了不支持IE 9以下的浏览器,其他主流的浏览器都支持. 3. 支持Promise API和URI Templates Promise是ES6的特性,Promise的中文含义为"先知",Pro

  • Python-Flask:动态创建表的示例详解

    今天小编从项目的实际出发,由于项目某一个表的数据达到好几十万条,此时数据的增删查改会很慢:为了增加提高访问的速度,我们引入动态创建表. 代码如下: from app_factory import app from sqlalchemy import Column, String, Integer class ProjectModel(app.db.model, app.db.Mixin): tablename = 'Project_' ID = Column(String(50), name='

  • MySQL中使用去重distinct方法的示例详解

    一 distinct 含义:distinct用来查询不重复记录的条数,即distinct来返回不重复字段的条数(count(distinct id)),其原因是distinct只能返回他的目标字段,而无法返回其他字段 用法注意: 1.distinct[查询字段],必须放在要查询字段的开头,即放在第一个参数: 2.只能在SELECT 语句中使用,不能在 INSERT, DELETE, UPDATE 中使用: 3.DISTINCT 表示对后面的所有参数的拼接取 不重复的记录,即查出的参数拼接每行记录

  • Oracle数据库创建存储过程的示例详解

    1.1,Oracle存储过程简介: 存储过程是事先经过编译并存储在数据库中的一段SQL语句的集合,调用存储过程可以简化应用开发人员的很多工作, 减少数据在数据库和应用服务器之间的传输,对于提高数据处理的效率是有好处的. 优点: 允许模块化程序设计,就是说只需要创建一次过程,以后在程序中就可以调用该过程任意次. 允许更快执行,如果某操作需要执行大量SQL语句或重复执行,存储过程比SQL语句执行的要快. 减少网络流量,例如一个需要数百行的SQL代码的操作有一条执行语句完成,不需要在网络中发送数百行代

  • Servlet开发JavaWeb工程示例详解

    一.什么是Servlet? Servlet是在服务器上运行的小程序,也就是一个Java类,但比较特殊,不需要new,自动就可以运行.也有创建.垃圾回收和销毁过程.Servlet是JavaWeb的三大组件之一(Servlet.Filter.Listener),它属于动态资源.Servlet的作用是处理请求,服务器会把接收到的请求交给Servlet来处理,在Servlet中通常需要: 接收请求数据: 处理请求: 完成响应. 例如客户端发出登录请求,或者输出注册请求,这些请求都应该由Servlet来完

  • 基于gin的golang web开发:路由示例详解

    Gin是一个用Golang编写的HTTP网络框架.它的特点是类似于Martini的API,性能更好.在golang web开发领域是一个非常热门的web框架. 启动一个Gin web服务器 使用下面的命令安装Gin go get -u github.com/gin-gonic/gin 在代码里添加依赖 import "github.com/gin-gonic/gin" 快速启动一个Gin服务器的代码如下 package main import "github.com/gin-

  • springboot项目配置swagger2示例详解

    swagger简介 Swagger是一款RESTful接口的文档在线自动生成.功能测试功能框架.一个规范和完整的框架,用于生成.描述.调用和可视化RESTful风格的Web服务,加上swagger-ui,可以有很好的呈现. 当我们在后台的接口修改了后,swagger可以实现自动的更新,而不需要人为的维护这个接口进行测试. 一.swagger2中常用的注解作用 注解 作用 @Api 修饰整个类,描述Controller的作用 ,表示标识这个类是swagger的资源 @ApiOperation 描述

  • MySQL教程数据定义语言DDL示例详解

    目录 1.SQL语言的基本功能介绍 2.数据定义语言的用途 3.数据库的创建和销毁 4.数据库表的操作(所有演示都以student表为例) 1)创建表 2)修改表 3)销毁表 如果你是刚刚学习MySQL的小白,在你看这篇文章之前,请先看看下面这些文章.有些知识你可能掌握起来有点困难,但请相信我,按照我提供的这个学习流程,反复去看,肯定可以看明白的,这样就不至于到了最后某些知识不懂却不知道从哪里下手去查. <MySQL详细安装教程> <MySQL完整卸载教程> <这点基础都不懂

随机推荐