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 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++学习记录 - operator new、new operator、delete等 -> 正文阅读

[C++知识库]C++学习记录 - operator new、new operator、delete等

文章部分内容来自侯捷老师的C++内存管理视频。

0. placement new

原地构造
用法:

int *p = new int(10);  // new operator
new(p) int(1000);   // 在 p 指向的内存上 构造一个int对象,即placement new
// 此时 *P 为 1000。

因为是在一个已有的空间上构造对象,所以,空间的释放,就由free或者delete负责了。

0.1 重载placement new

与后边的 3 :operator new的重载无异,就是可以多增加几个函数,但是,第一个参数必须是std::size_t 类型,这个参数接收的是 new 后边类型的大小,不用显式传参,默认会传递。
重载示例:

#include <iostream>

using std::cout, std::endl;

class base{

public:
    base(){
        cout << "ctor" << endl;
    }

    ~base(){
        cout << "dtor" << endl;
    }

    // 标准库中的 placement new, 这里什么都没做,只是返回原来的指针。
    void * operator new(std::size_t size, void *p){
        cout << "std placement new" << endl;
        return p;
    }

    //重载 placement new,附加了一个参数 a,参数size会默认传递,不用显式传参。
    void * operator new(std::size_t size, int a){
        cout << "a = " << a << endl;
        return malloc(size);
    }

    void * operator new(std::size_t size){
        cout << "new diy" << endl;
        return malloc(size);    
    }

    void operator delete(void *p){
        cout << "delete diy" << endl;
        free(p);
    }

private:
    int a, b, c;
};


int main(){
    base p;
    new(&p) base;
    new(4) base;

    return 0;
}


运行结果:

ctor  // 对象默认的构造函数输出的
std placement new  // 默认placement new 输出的
ctor // 默认placement new 输出的
a = 4 // 重载的placement new 输出的
ctor  // 重载的placement new 输出的
dtor  // 析构

1. new operator

一般情况下,我们使用的new都是new operator
用法:

int *p = new int(3);

其实底层有两个过程:
第一个过程:开辟空间
第二个过程:构造对象

2. delete operator

delete operator就是平时常用的delete,与new 搭配成对使用。
用法:

int *p = new int;
delete p;

3. operator new

是全局的库函数。

用法:

void* operator new(size_t size);

其实,operator new底层调用的是malloc(调用形式也挺像的,传入一个开辟的Byte数,返回一个void *)。

  • 注意,operator new 支持重载!当然了,如果重载了,那就要重载对应的operator delete

4. operator delete

是全局的库函数,与operator new搭配使用。

用法:

int *p = new int;
operator delete(p);

底层调用的是free

  • 支持重载。

5. new 做了哪些事情?

new 操作可以大致的认为包含了两个过程:
第一个过程:开辟相应的空间(使用operator new)。
第二个过程:在刚开辟的空间内构造一个对象(placement new)。

模拟new操作。

int *p = static_cast<int*>( operator new(sizeof(int)) );   // new operator 开辟空间
new(p) int(100);  // placement new 在p指向的内存上构造一个对象

上边说到operator newoperator delete支持重载,重载可以为我们自己的类类型定制化的分配内存。
只需要在类内重载operator newoperator delete

编译器遇到new和delete时,首先看对应的类类型中是否有重载的operator newoperator delete,如果有,则优先调用重载的版本去分配内存,然后再原地构造。如果没有重载版本,就调用全局的库函数。

如果一个类内有重载的operator newoperator delete,会优先调用重载版本,代码如下:

#include <iostream>

using std::cout, std::endl;

class base{

public:
    base(){
        cout << "ctor" << endl;
    }

    ~base(){
        cout << "dtor" << endl;
    }


    void * operator new(std::size_t size){
        cout << "new diy" << endl;
        void *resPtr = malloc(size);
        return resPtr;        
    }

    void operator delete(void *p){
        cout << "delete diy" << endl;
        free(p);
    }

private:
    int a, b, c;
};


int main(){

    base *p = new base;

    delete p;

    return 0;
}

运行结果:

new diy
ctor
dtor
delete diy

也可以显式的使用全局的库函数来new一个对象出来,把main函数中的new和delete换成全局的库函数,如下:

base *p = ::new base;
::delete p;

运行结果:

ctor
dtor

显式的调用了全局的库函数,而没有调用类类型重载的版本。

当然,如果不在类内进行重载,在全局进行重载,也是可以的。

这个两个函数,在类内重载,必须是静态的,即有static修饰,因为调用这两个方法时,对象还没创建(调用new时)或者已经析构(调用delete时),this指针此时是无效的,再想去通过this调用一个方法,这是不可能的,所以必须是静态的。

上边的代码中,没有加static修饰,也可以完成操作,是因为编译器替我们加上了static

6. array new 与 array delete

int *p = new int[10];
delete[] p;

array new即是用new申请一个数组空间,此时 p 指向第一个元素的起始位置,释放时必须使用对应的array delete,也就是delete[ ]

对于基本数据类型来说,如果把上边的delete[] p 改成 delete p,没什么问题,不会造成内存泄漏。但如果是自定义的类,并且类中有在堆上开辟内存空间的话,就会造成内存泄漏。
因为delete p 只会调用一个元素的析构函数,然后释放对应的空间,数组中的其余元素,虽然元素(类对象)本身的空间释放了,但是该对象原本来堆上开辟的内存空间,并没有释放掉(因为他的析构函数没有被调用),由此造成内存泄漏。

与上边的new 和 delete一样,array版本的new和delete也支持重载。
示例如下:

#include <iostream>

using std::cout, std::endl;

class base{

public:
    base(){
        cout << "ctor" << endl;
    }

    ~base(){
        cout << "dtor" << endl;
    }

    void * operator new[](std::size_t size){
        cout << "new[] diy" << endl;
        void *resPtr = malloc(size);
        return resPtr;        
    }

    void operator delete[](void *p){
        cout << "delete[] diy" << endl;
        free(p);
    }

private:
    int a, b, c;
};


int main(){
    base *p = new base[2];
    delete[] p;
    return 0;
}

运行结果:

new[] diy
ctor
ctor
dtor
dtor
delete[] diy

7. delete[ ] 为什么能够释放数组?

在使用new创建一个数组时,所开辟的实际内存大小比要申请的大小要大一些,在申请到的数组空间的上下都有额外的开销,来记录这块数组空间的相关信息,其中就包括数组空间的长度,每个元素的长度。所以 delete[ ] 可以顺利的知道数组的长度,并且可以成功的调用所有元素的析构函数。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-21 15:10:21  更:2021-08-21 15:11:34 
 
开发: 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年11日历 -2024/11/18 13:56:10-

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