分类
未分类

c++ 中 malloc 与 new 的几点区别

在 c++ 中,malloc 与 new 都是分配内存可以用的方法,它们在使用中也存在不少区别,整理了下,主要可分为以下几个方面:


malloc 与 free 是 c 的库函数,而 new 和 delete 是 c++ 中的运算符。在 c++ 中,new 与 delete 是可以被重载的。 自定义重载 new 和 delete 主要是为了防止内存泄漏问题。


malloc 返回的是 void* 指针,因此调用 malloc 申请指定类型的内存对象时,需要进行对应的强制类型转换。而 new 返回的即为调用它的类型的指针。比如:

int* ptr1 = (int*)(malloc(sizeof(int)));
int* ptr2 = new int;
free(ptr1);
delete ptr2;

单对象申请内存时, malloc 需要计算出分配空间大小, 而 new 不需要。为数组分配空间时,两者的调用方式也不尽相同,比如分配一个长度为10的整型数组空间:

int* ptrArray1 = (int*)(malloc(sizeof(int) * 10);
int* ptrArray2 = new int[10];
free(ptrArray1);
delete[] ptrArray2;

在面向对象中, malloc 执行并不会调用类的构造函数,而 new 会调用类构造函数。上一段示例代码:

class Test
{
public:
    int x = 3;
    void print()
    {
        cout << hex << x << endl;
    }
}
int main()
{
    Test* pTest1 = (Test*)malloc(sizeof(Test));
    pTest1 -> print();
    Test* pTest2 = new Test;
    pTest2 -> print();
    free(pTest1);
    delete pTest2;
    return 0;
}

运行结果是

cdcdcdcd
3

顺便提一下 Visual Studio 以及 VC 中对未初始化的堆内存以及栈内存的处理。在 Debug 模式下,编译器会将未初始化的堆内存填充 0xCD ,而对未初始化的栈内存填充 0xCC 。简体中文 Windows 下使用的是 GBK 编码,一连串的 0xCDCDCDCD 就会被解析为“屯屯屯屯”,而一连串的 0xCCCCCCCC 会被解析为“烫烫烫烫”,这就是不小心使用了未被初始化的堆栈内存导致的。在上面的代码中,由于 malloc 不会调用类的构造函数,为它分配的堆内存单元被填充了 0XCD,会有上面的输出结果。而 new 运算符会调用默认构造函数(上面代码是成员变量默认初始化,本质上一样)。下面上一段测试未被分配的栈内存的代码:

class Test
{
public:
    int x;
    void print()
    {
        cout << hex << x << endl;
    }
}
int main()
{
    Test test = new Test;
    test.print();
    return 0;
}

运行结果是

cccccccc

如果想用 malloc 并调用对象的构造函数,有如下两种方案。 1. 显示调用构造函数 2. 使用 placement new

示例代码如下:

class Test
{
public:
    int x = 3;
    void print()
    {
        cout << hex << x << endl;
    }
}
int main()
{
    Test* pTest1 = (Test*)malloc(sizeof(Test));
    pTest1 -> print();
    Test* pTest2 = (Test*)malloc(sizeof(Test));
    pTest2 -> Test::Test();
    pTest2 -> print();
    Test* pTest3 = (Test*)malloc(sizeof(Test));
    new(pTest3)Test();
    pTest3 -> print();
    free(pTest1);
    free(pTest2);
    pTest3 -> ~Test();
    free(pTest3);
    return 0;
}

运行结果是

cdcdcdcd
3
3

第一种方式比较容易理解,placement new 实际上完成了 new 调用构造函数的过程,它是在已经开辟好的内存空间上调用类的构造函数。placement new 不应该使用delete,因为它是完成了调用构造函数的过程,应该调用对象的析构函数。


分配内存失败后的返回值不同。

malloc 分配内存失败后会返回 NULL,而使用 new 时分配空间失败会抛出 bad_alloc 异常,需要使用 try catch 捕获 。不过,可以借助全局 new 的另外一个重载形式:

void* operator new(std::size_t, std::nothrow_t const &) throw();

来解决此问题。 重载形式的示例代码如下

int* ptr = new(std::nothrow) int;
if(ptr == NULL)
{
    //todo
}

这样,就可以像 malloc 一样,通过 NULL 来判断内存是否分配成功了。


是否可以直接扩充内存。

在分配内存空间后,如果在使用过程中发现内存空间不足需要扩充,malloc 分配的空间可以使用 realloc 直接扩容,而 new 的不可以。

int main()
{
    int* array = (int*)(malloc(sizeof(int) * 3));
    cout << hex << array[3] << endl;
    array = (int*)(realloc(array, sizeof(int) * 5));
    cout << hex << array[3] << endl;
    free(array);
    return 0;
}

运行结果是:

fdfdfdfd
cdcdcdcd

上述代码中,malloc 首先分配了三个连续的整型内存空间,此时输出 array[3] 是未定义的行为,而 realloc 将内存空间直接扩充为五个连续整型,此时输出结果是未初始化的堆内存,也就是0xCD。

以上就是部分 malloc 与 new的不同之处,在 oop 中,还是推荐 new 比较多一些,但 malloc 也有自己的部分优势。

发表评论

电子邮件地址不会被公开。 必填项已用*标注