C++(malloc/free和new/delete)

C++(malloc/free和new/delete)

用于测试的类A

/**
 * @brief 用于检测new和delete对构造函数和析构函数
 * 
 */
class A{
    public:
        A(){
            std::cout<<"call constructor:A()"<<std::endl;
        }
        ~A(){
            std::cout<<"call destructor:A()"<<std::endl;
        }
};

new和delete的使用

1、new和delete关于构造函数和析构函数的调用

/**
 * @brief 用于探索delete数组时的情况.用delete[]和delete
 * 函数输出:
delete[]
call constructor:A()
call constructor:A()
call destructor:A()
call destructor:A()
delete
call constructor:A()
call constructor:A()
call destructor:A()
munmap_chunk(): invalid pointer
Aborted (core dumped)
 */
void test1(){
    std::cout<<"delete[]"<<std::endl;
    A *twoA = new A[2];
    delete[] twoA;
    
    std::cout<<"delete"<<std::endl;
    twoA = new A[2];
    delete twoA;
    std::cout<<std::endl;
}

可见,new一个对象就会调用一次构造函数,delete一个对象就会调用一次析构函数。但是要delete数组时,必须使用,否则会出现报错:munmap_chunk(): invalid pointer
Aborted (core dumped)

2、模仿new失败的情况:申请一个很大的数组

/**
 * @brief 探索new失败的情况
抛出异常:
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)
 */
void test4(){
    //申请失败抛异常
	int* ptr1 = new int[(long long)9999999999999999]; 
}

抛出异常:std::bad_alloc

3、对同一个对象delete两次

/**
 * @brief 对同一个对象delete两次
 * 函数输出:
call constructor:A()
call destructor:A()
call destructor:A()
free(): double free detected in tcache 2
Aborted (core dumped)
 */
void test5(){
    A *a = new A();
    delete a;
    delete a;
}

可见,delete两次后会出现无法解决的报错:free(): double free detected in tcache 2。注意:虽然delete两次不合法,但是根据输出可知析构函数还是调用了两次

4、尝试使用delete后的空间

/**
 * @brief 尝试使用delete后的空间
 * 函数输出
call constructor:A()
call destructor:A()
call constructor:A()
before deelte int->i : 100
after deelte int->i : 1498309783
call destructor:A()
 */
void test6(){
    A *a = new A();
    int *i = new int(100);
    
    delete a;
    // delete后,尝试访问a
    A a_copy = *a;
    a = new A();

    printf("before deelte int->i : %d\n", *i);
    delete i;
    printf("after deelte int->i : %d\n", *i);
}

可见,delete之后的对象还是可以继续访问(不报错),但是内容是未知的。

malloc和free的使用

1、malloc和free关于构造函数和析构函数的调用

/**
 * @brief 用于探索free数组时的情况.用delete[]和delete
 * 函数无输出:证明malloc没有调用构造函数、free没有调用析构函数
 */
void test2(){
    A *twoA = (A*)malloc(sizeof(A)*2);
    free(twoA);
}

可见,malloc没有调用构造函数、free没有调用析构函数。

2、模仿malloc失败的情况:申请一个很大的数组

/**
 * @brief malloc失败的情况
 * 函数输出:ptr1 is NULL
 */
void test8(){
    //申请失败抛异常
	int* ptr1 = (int*)malloc(sizeof(int) * (long long)9999999999999999);
    if (ptr1 == NULL){
        printf("ptr1 is NULL\n");
    }
}

可见,malloc失败并不会像new失败一样抛出异常。只是返回NULL。

3、尝试使用free后的空间

/**
 * @brief 用于探索free后的空间是否可以继续使用
 * 函数输出
before free :
 nums[0] = 0
 nums[1] = 1
after free :
 nums[0] = 1575797714
 nums[1] = 5
after free and assign :
 nums[0] = 2
 nums[1] = 2
 */
void test3(){
    int *nums = (int*)malloc(sizeof(int)*2);
    nums[0] = 0; nums[1] = 1;
    printf("before free :\n nums[0] = %d\n nums[1] = %d\n", nums[0], nums[1]);
    free(nums);
    printf("after free :\n nums[0] = %d\n nums[1] = %d\n", nums[0], nums[1]);
    nums[0] = nums[1] = 2;
    printf("after free and assign :\n nums[0] = %d\n nums[1] = %d\n", nums[0], nums[1]);
}

可见,free后的数组依然可以访问和赋值。只是访问free后的数组内容是未知的。

4、对同一个对象free两次

/**
 * @brief 对同一个对象free两次 
 * 函数输出:
 free(): double free detected in tcache 2
Aborted (core dumped)
 * 
 */
void test7(){
    int *i = new int(100);
    printf("before free, i = %d\n", *i);
    free(i);
    printf("after first free, i = %d\n", *i);
    free(i);
    printf("after second free, i = %d\n", *i);
}

可见,对于同一个对象free两次,会报错:

free(): double free detected in tcache 2
Aborted (core dumped)

且printf语句没有打印。

总结

new和delete

  • new和delete是运算符,不需要包含任何头文件即可使用
  • new的工作有两步:
    1. 申请空间
    2. 对空间进行初始化(调用类的构造函数)
  • delete的工作有两步:
    1. 对空间进行资源清理(调用类的析构函数)
    2. 释放空间
  • new失败时会抛出异常

malloc和free

  • malloc和free是函数
  • malloc需要传入空间的大小,并且返回的是void*,需要手动强制类型转换
  • malloc和free都只是简单的申请和释放空间

两者的联系

相同点

  1. new和malloc都是从堆申请空间
  2. delete和free都是释放空间,并且都是将内存标记为可用内存,释放后可以继续访问
  3. 对同一对象重复free、delete都是会报错的

不同点

  1. new和delete会调用构造(析构)函数,而malloc和free不会
  2. malloc需要指定计算空间大小和强制类型转换,而new不需要
  3. malloc失败是返回NULL,而new失败是抛出异常

使用技巧

在c++中尽量使用new和delete,避免使用malloc和free。

本文的全部代码

/**
 * @file test1.cpp
 * @author your name (you@domain.com)
 * @brief 
 * @version 0.1
 * @date 2022-08-15
 * 
 * @copyright Copyright (c) 2022
 * 
 */

#include<iostream>
/**
 * @brief 用于检测new和delete对构造函数和析构函数
 * 
 */
class A{
    public:
        A(){
            std::cout<<"call constructor:A()"<<std::endl;
        }
        ~A(){
            std::cout<<"call destructor:A()"<<std::endl;
        }
};
/**
 * @brief 用于探索delete数组时的情况.用delete[]和delete
 * 函数输出:
delete[]
call constructor:A()
call constructor:A()
call destructor:A()
call destructor:A()
delete
call constructor:A()
call constructor:A()
call destructor:A()
munmap_chunk(): invalid pointer
Aborted (core dumped)
 */
void test1(){
    std::cout<<"delete[]"<<std::endl;
    A *twoA = new A[2];
    delete[] twoA;
    
    std::cout<<"delete"<<std::endl;
    twoA = new A[2];
    delete twoA;
    std::cout<<std::endl;
}

/**
 * @brief 用于探索free数组时的情况.用delete[]和delete
 * 函数无输出:证明malloc没有调用构造函数、free没有调用析构函数
 */
void test2(){
    A *twoA = (A*)malloc(sizeof(A)*2);
    free(twoA);
}

/**
 * @brief 用于探索free后的空间是否可以继续使用
 * 函数输出
before free :
 nums[0] = 0
 nums[1] = 1
after free :
 nums[0] = 1575797714
 nums[1] = 5
after free and assign :
 nums[0] = 2
 nums[1] = 2
 */
void test3(){
    int *nums = (int*)malloc(sizeof(int)*2);
    nums[0] = 0; nums[1] = 1;
    printf("before free :\n nums[0] = %d\n nums[1] = %d\n", nums[0], nums[1]);
    free(nums);
    printf("after free :\n nums[0] = %d\n nums[1] = %d\n", nums[0], nums[1]);
    nums[0] = nums[1] = 2;
    printf("after free and assign :\n nums[0] = %d\n nums[1] = %d\n", nums[0], nums[1]);
}

/**
 * @brief 探索new失败的情况
抛出异常:
terminate called after throwing an instance of 'std::bad_alloc'
  what():  std::bad_alloc
Aborted (core dumped)
 */
void test4(){
    //申请失败抛异常
	int* ptr1 = new int[(long long)9999999999999999]; 
}

/**
 * @brief 对同一个对象delete两次
 * 函数输出:
call constructor:A()
call destructor:A()
call destructor:A()
free(): double free detected in tcache 2
Aborted (core dumped)
 */
void test5(){
    A *a = new A();
    delete a;
    delete a;
}
/**
 * @brief 尝试使用delete后的空间
 * 函数输出
call constructor:A()
call destructor:A()
call constructor:A()
before deelte int->i : 100
after deelte int->i : 1498309783
call destructor:A()
 */
void test6(){
    A *a = new A();
    int *i = new int(100);
    
    delete a;
    // delete后,尝试访问a
    A a_copy = *a;
    a = new A();

    printf("before deelte int->i : %d\n", *i);
    delete i;
    printf("after deelte int->i : %d\n", *i);
}

/**
 * @brief 对同一个对象free两次 
 * 函数输出:
 free(): double free detected in tcache 2
Aborted (core dumped)
 * 
 */
void test7(){
    int *i = new int(100);
    printf("before free, i = %d\n", *i);
    free(i);
    printf("after first free, i = %d\n", *i);
    free(i);
    printf("after second free, i = %d\n", *i);
}

/**
 * @brief malloc失败的情况
 * 函数输出:ptr1 is NULL
 */
void test8(){
    //申请失败抛异常
	int* ptr1 = (int*)malloc(sizeof(int) * (long long)9999999999999999);
    if (ptr1 == NULL){
        printf("ptr1 is NULL\n");
    }
}

int main(){
    // test1();
    // test2();
    // test3();
    // test4();
    // test5();
    // test6();
    // test7();
    test8();
    return 0;
}
posted @ 2022-08-15 17:49  DavidJIAN  阅读(66)  评论(0)    收藏  举报