返回项目列表

/note/ICPC笔记

ICPC笔记

当前项目读取 content/notes/ICPC笔记 下的 Markdown 文件。

ICPC笔记

new与delete

new与delete new:向内存申请一块空间,并创建对象。 delete:释放 new 申请的空间,并销毁对象。 它们主要用于 动态内存管理 。 1.new 平时写: 这个变量一般存在于 栈区 ,函数结束后自动销毁。 但有时候我们需要: 这时候 new int 会在 堆区 申请一个 int 大小...

new与delete

new:向内存申请一块空间,并创建对象。 delete:释放 new 申请的空间,并销毁对象。

它们主要用于 动态内存管理


1.new

平时写:

int x = 10;

这个变量一般存在于 栈区,函数结束后自动销毁。

但有时候我们需要:

int* p = new int;

这时候 new int 会在 堆区 申请一个 int 大小的空间,返回这个空间的地址。


基本写法

申请一个普通变量

int* p = new int;
*p = 10;

cout << *p << endl;

delete p;

申请时直接初始化

int* p = new int(10);

cout << *p << endl;

delete p;

等价于:

int* p = new int;
*p = 10;

2.delete

delete 用来释放 new 申请的内存。

如果你只写:

int* p = new int(10);

但是不写:

delete p;

那么这块内存就一直占着,程序无法再使用它。

这叫 内存泄漏

比如:

void f() {
    int* p = new int(10);
}

这里函数结束后,p 这个指针变量消失了。

但是 new int(10) 申请出来的那块堆内存还在。

问题是:

p 没了

所以你再也找不到那块内存了,也就无法释放。

这就是内存泄漏。


3.数组

动态申请数组:

int* a = new int[5];

这表示申请 5 个 int 的连续空间。

释放数组必须写:

delete[] a;

完整代码:

#include <iostream>
using namespace std;

int main() {
    int* a = new int[5];

    for (int i = 0; i < 5; i++) {
        a[i] = i + 1;
    }

    for (int i = 0; i < 5; i++) {
        cout << a[i] << " ";
    }

    delete[] a;

    return 0;
}

输出:

1 2 3 4 5

4.new与delete基本规则

申请方式释放方式
new intdelete p
new int[10]delete[] p
new Studentdelete p
new Student[10]delete[] p

5.new 对象时会调用构造函数

假设有一个类:

#include <iostream>
using namespace std;

class Student {
public:
    Student() {
        cout << "构造函数调用" << endl;
    }

    ~Student() {
        cout << "析构函数调用" << endl;
    }
};

int main() {
    Student* p = new Student;

    delete p;

    return 0;
}

输出:

构造函数调用
析构函数调用

这说明:

new Student

不仅申请内存,还会调用构造函数。

delete p

不仅释放内存,还会调用析构函数。


6. new 对象数组

Student* arr = new Student[3];

delete[] arr;

这里会调用 3 次构造函数,释放时调用 3 次析构函数。

例子:

#include <iostream>
using namespace std;

class Student {
public:
    Student() {
        cout << "构造函数" << endl;
    }

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

int main() {
    Student* arr = new Student[3];

    delete[] arr;

    return 0;
}

输出类似:

构造函数
构造函数
构造函数
析构函数
析构函数
析构函数

7. new intnew int()

这个细节容易混。

int* p1 = new int;
int* p2 = new int();

区别:

new int

申请空间,但不初始化,里面是随机值。

new int()

申请空间,并初始化为 0。

例子:

int* p1 = new int;
int* p2 = new int();

cout << *p1 << endl; // 随机值
cout << *p2 << endl; // 0

也可以指定值:

int* p = new int(100);

8. 数组初始化

int* a = new int[5];

这 5 个数没有初始化,可能是随机值。

int* b = new int[5]();

这 5 个数都会初始化为 0。

例子:

int* a = new int[5]();

相当于:

a[0] = 0;
a[1] = 0;
a[2] = 0;
a[3] = 0;
a[4] = 0;

9. delete 后指针会怎样?

比如:

int* p = new int(10);

delete p;

执行完 delete p 后,堆区的内存被释放了。

但是注意:

p 本身仍然保存着原来的地址

它变成了一个 悬空指针

比如:

cout << *p << endl; // 危险

这时候 p 指向的内存已经无效了,再访问就是未定义行为。

所以推荐写:

delete p;
p = nullptr;

完整写法:

int* p = new int(10);

delete p;
p = nullptr;

这样后面如果误用:

if (p != nullptr) {
    cout << *p << endl;
}

delete nullptr 是安全的

下面这样没问题:

int* p = nullptr;

delete p;

不会报错。


10.不能重复 delete

错误写法:

int* p = new int(10);

delete p;
delete p; // 错误

第一次 delete 已经释放了内存,第二次再释放同一块内存,叫 重复释放,很危险。

正确:

int* p = new int(10);

delete p;
p = nullptr;

delete p; // 安全,因为 delete nullptr 没事

11. new 失败?

如果内存不够,普通的 new 会抛出异常:

std::bad_alloc

比如:

int* p = new int[1000000000000];

可能失败。

如果你不想让它抛异常,可以写:

int* p = new(nothrow) int[1000000000000];

if (p == nullptr) {
    cout << "申请失败" << endl;
}

需要头文件:

#include <new>

完整写法:

#include <iostream>
#include <new>
using namespace std;

int main() {
    int* p = new(nothrow) int[1000000000000];

    if (p == nullptr) {
        cout << "申请失败" << endl;
    } else {
        delete[] p;
    }

    return 0;
}

不过你现在初学 C++,先记住普通 new 就够了。


12.和类的构造函数结合

例如:

class Student {
private:
    string name;
    int age;

public:
    Student(string n, int a) : name(n), age(a) {}

    void show() {
        cout << name << " " << age << endl;
    }
};

普通对象写法:

Student s("Tom", 18);
s.show();

动态对象写法:

Student* p = new Student("Tom", 18);
p->show();

delete p;

这里:

p->show();

等价于:

(*p).show();

因为 p 是指针,所以访问成员函数要用 ->


13.现代 C++ 不推荐乱用裸 new/delete

现在更推荐用:

vector
string
unique_ptr
shared_ptr

比如动态数组:

以前:

int* a = new int[n];
delete[] a;

现在更推荐:

vector<int> a(n);

对象指针:

以前:

Student* p = new Student;
delete p;

现在更推荐:

auto p = make_unique<Student>();