C++ new/delete 使用场景

new 和 delete 是 C++ 中非常重要的两个关键字,其作用是实现动态对象的管理。正确掌握它们的使用方法对于有效管理程序的内存、提高性能,以及避免内存泄漏等问题至关重要,是编写健壮 C++ 程序的核心技能。

1. 创建/销毁对象

在 C++ 中,newdelete 运算符常用于动态对象的创建和销毁。这一节,我们需要了解:

  1. 单个对象的创建和销毁过程
  2. 对象数组的创建和销毁过程

#if 1
#include <iostream>
using namespace std;


struct Demo
{
	Demo()
	{
		cout << "Demo 构造函数" << endl;
		pArr = new int[100];
	}

	void do_logic()
	{
		cout << "Demo Logic 函数" << endl;
	}

	~Demo()
	{
		cout << "Demo 析构函数" << endl;
		delete pArr;
	}

	int* pArr;
};

// 1. 单个对象
void test01()
{
	Demo* demo = new Demo;
	delete demo;
}

// 2. 对象数组
void test02()
{
	Demo* demo = new Demo[3];
	delete[] demo;
	// delete demo;
	// free(demo);
	
	int* data = new int[3];
	// delete[] data;
	// delete data;
	// free(data);

	// 为什么 demo 使用 delete 或 free 会导致程序崩溃?
	// 为什么 data 使用 delete 或 free 不会导致程序崩溃?
}


int main()
{
	test02();
	return 0;
}
#endif

对象数组的内存结构左图,内置类型数组的内存结构如右图。

2. 分配/释放内存

在 C 语言中,我们使用 malloc 和 free 实现象内存的分配和释放。虽然这两个函数在 C++ 中也适用,但是我们很少直接使用,而是使用 operator new 和 operator delete 函数。接下来,我们通过三个问题,来理解 operator new 和 operator delete 函数的用法和更多细节。

  1. new/delete 和 operator new/delete 的区别是什么?
  2. malloc/free 和 operator new/delete 的区别是什么?
  3. new handler 如何使用?

#if 0
#include <iostream>
using namespace std;

struct Demo
{
    Demo()
    {
        cout << "Demo 构造函数" << endl;
    }

    ~Demo()
    {
        cout << "Demo 析构函数" << endl;
    }
};

// 1. new/delete 和 operator new/delete 的区别是什么?
// 1.1 new/delete 是运算符,用于对象的实例化和销毁
// 1.2 operator new/delete 是函数,用于内存的申请和释放
// 1.2 new/delete 的内存申请通过调用 operator new/delete 实现
void test01()
{
    /*
    00007FF7499825E1  call        operator new (07FF74998104Bh)
    00007FF7499825FE  call        Demo::Demo (07FF7499812DAh)
    */
    Demo* demo = new Demo;

    /*
    00007FF7660423EE  call        Demo::~Demo (07FF766041082h)
    00007FF76604240D  call        operator delete (07FF7660413EDh)
    */
    delete demo;
}


// 2. malloc/free 和 operator new/delete 的区别是什么?
// 2.1 两者都是用于内存申请和释放函数
// 2.2 operator new/delete 是对 malloc/free 函数的封装
// 2.3 内存申请失败时,malloc 返回空指针,operator new 则执行 new handler,或抛出异常
void test02()
{
    void* p1 = malloc(100);
    free(p1);

    void* p2 = operator new(100);
    operator delete(p2);
}


void my_new_handler()
{
    cout << "my new handler 函数调用" << endl;
}


// 3. new handler 如何使用?
// 3.1 new handler 是内存申请失败时回调的处理函数
// 3.2 使用 set_new_handler 设置回调处理函数 void(*)()
// 3.3 该函数内部一般执行释放资源、日志记录、抛出异常等操作
// 注意:提供 new_handler 之后,可能会陷入无限循环
void test03()
{
    // 设置 new handler 函数
    set_new_handler(my_new_handler);

    void* ptr = operator new(100000000000000000);
}


int main()
{
    test03();
    return 0;
}

#endif

3. 初始化/清理对象

我们在进行对象创建时,由 new 自动申请内存,并进行对象的初始化。假设,我已有内存,只希望在我持有的内存上初始化对象(即:在指定内存位置调用对象构造函数),这个如何实现?

在 C++ 中,构造函数不能够直接手动调用,但可以通过 placement new 来实现。

有对象初始化就会对应对象清理,由于析构函数是可以直接调用,对于在指定内存位置清理对象,就相对比较简单。

所以,这一节,我们需要学习以下几个问题:

  1. 如何在指定内存位置构造和清理对象?
  2. new 和 placement new 的异同是什么?
  3. placement new 使用时,有什么注意点?

#if 1
#include <iostream>
using namespace std;


struct Demo
{
    Demo()
    {
        cout << "Demo 构造函数" << endl;
    }
    Demo(int, int)
    {
        cout << "Demo 有参构造" << endl;
    }
    void do_logic()
    {
        cout << "Demo Logic 函数" << endl;
    }
    ~Demo()
    {
        cout << "Demo 析构函数" << endl;
    }
};


// 1. 如何在指定内存位置构造和清理对象?
void test01()
{
    // 1. 在堆上
    void* ptr = operator new(sizeof(Demo));
    Demo* demo = new(ptr) Demo(10, 20);

    demo->do_logic();

    demo->~Demo();
    operator delete(ptr);


    // 2. 在栈上
    char buf[32] = { 0 };
    Demo* d = new(buf) Demo;

    d->do_logic();
    d->~Demo();
}

// 2. new 和 placement new 的异同是什么?
// 2.1 new 和 placement new 两者都是运算符,placement new 是一个带参数版本的 new 
// 2.2 两者都遵循对象的构造函数:operator new + 构造函数
// 2.3 new 分配内存 + 构造函数,placement new 不分配内存 + 构造函数
void test02()
{
    char buf[32] = { 0 };
    Demo* d = new(buf) Demo;
}

// 3. placement new 使用时,有什么注意点?
// 3.1 确保内存有效性,一定要保证内存大小和位置合法,避免越界操作
// 3.2 内存对齐,确保分配的内存地址满足对象的对齐要求
// 3.3 小心处理内存管理,例如:避免重复构造,可能会导致内存泄漏
struct Sample
{
    Sample()
    {
        cout << "Sample 构造函数" << endl;
        int* p = new int[100000];
    }
    ~Sample()
    {
        if (p != nullptr)
        {
            delete[] p;
        }
        cout << "Sample 析构函数" << endl;
    }
    int* p{ nullptr };
};

void test03()
{
    char buf[32] = { 0 };
    Sample* sample = nullptr;
    for (;;)
    {
        sample = new (buf) Sample;
    }
    sample->~Sample();
}


int main()
{
    test03();
    return 0;
}

#endif

未经允许不得转载:一亩三分地 » C++ new/delete 使用场景
评论 (0)

9 + 5 =