C++ note (1)
Eiaton

newdelete 、动态创建变量/数组、内存泄漏、 = default

newdelete 关键字

new 总结

  1. ptr_type* ptr_name = new ptr_type; // 动态创建一个对象
  2. ptr_type* ptr_name = new ptr_name;(初始化参数) ; // 动态创建一个对象,可不初始化
  3. ptr_type* ptr_name = new ptr_type[arr_length] {Init_list}; // 用于动态分配数组,可没有初始化列表(string 类型就一定要有)
  • 类型可为基本类型,也可为类类型(class type)。
    • 若为类类型,则初始化参数相当于将实际参数传递给该类的构造函数
  • new 运算返回一个该类型指针(不是 **void***),指向分配到的内存空间
  • 若内存分配失败,抛出异常结束程序,而不是返回 NULL

delete 总结:

  1. delete 变量名; //基本用法
  2. delete []变量名; //用于释放数组
  • 如果动态分配了一个数组,但是却用delete p的方式释放,没有用[],则

    • 编译时没有问题,运行时也一般不会发生错误,
    • 但实际上会导致动态分配的数组没有被完全释放。
  • delete 释放的是指针所指对象占据的内存。

    • delete 对象指针,会调用该对象的析构函数。([] 将令其中所有元素都调用各自析构函数)
    • 用delete释放空间后,指针的值仍是原来指向的地址,但指针已无效(重复释放将出错,即非法指针访问)。
  • delete 本身会自动检查对象是否为空。如果为空,就不做操作,因此 delete 空指针不需要特判。

  • 为防止重复删除出错,最好删除后就把指针赋为空。

动态创建变量/数组

动态创建一个整型变量

1
2
3
int *p = new int;
cin >> *p;
delete p;

动态创建一个整型数组

1
2
3
4
5
6
7
int *p;
p = new int[length] {1, 2, 3};
// 若没有初始化列表,则不确定值;若初始化列表长度不够,则自动补齐`0`
for (int i = 0; i < length; i++) {
cout << p[i] << " ";
}
delete [] p;

动态创建一个 string 数组

1
2
3
4
5
6
7
8
9
10
11
int n = 3;
string *p = new string[n] {
string(5, 'a'), string(4, 'b'), string(3, 'c')
};
// 由于将决定分配多少长度,`string`创建时必须初始化
p[1][2] = 'f';
// 初始化后可以更改
for (int i = 0; i < n; i++) {
cout << p[i] << endl;
}
delete [] p;

动态创建一个二维整型数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int row;
int col;
cin >> row >> col;
int **a;
a = new int *[row];
for (int i = 0; i < row; i++) {
a[i] = new int [col];
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
cin >> a[row][col];
}
}
for (int i = 0; i < row; i++) {
delete [] a[i];
}
delete [] a;

内存泄漏

是指 new 的指针丢失导致占用内存无法释放

  1. 指针赋值时
    1
    2
    3
    int *p = new int(7);
    p = nullptr;
    // 内存泄漏
  2. 指针离开作用域时
    1
    2
    3
    4
    void fn() {
    int *p = new int(7);
    }
    // 内存泄露
  3. 异常导致程序或函数终止
    1
    2
    3
    4
    5
    6
    void fn() {
    int *p = new int(7);
    g();
    delete p;
    }
    // 若 g() 抛出异常则内存泄漏

显式默认化函数定义 = default

用于恢复函数的默认定义
当我们声明有参构造函数时,编译器就不会创建默认构造函数。为了使编译器创建
该默认构造函数,可以在函数声明后指定 = default

  • 若以 = default 声明,则该函数不能写实现
  • 特殊成员函数包括:
    • 默认构造函数
    • 析构函数
    • 复制构造函数等

例如:(有参时用前面的,无参时用默认,这时不能写A()的实现)

1
2
3
4
5
public:
A(int x) {
cout << "constructed";
}
A() = default;