我遇到了一个无法解决的问题。
我的问题是,如果我使用malloc
来分配内存,然后使用delete
删除内存块?一般经验法则是
如果我们使用malloc分配内存,则应使用free删除内存。
如果我们使用new分配内存,则应使用delete将其删除。
现在,为了检查如果我们做相反的事情会发生什么,我写了一个小代码。
#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
class A
{
int p=10;
public:
int lol() {
return p;
}
};
int main()
{
int *a = (int *)malloc(sizeof(int)*5);
for(int i=0; i<4; i++) {
a[i] = i;
cout << a[i] << endl;
}
delete(a);
// Works fine till here..
A b;
cout << b.lol() << endl;
free(b); // Causes error.
return 0;
}
我得到的错误是:
错误:无法将参数"1"的"A"转换为"void*"自由(无效*)
我不明白为什么会发生这种事。请解释。
当您将delete
调用为指针时,编译器会自动为您调用该类的dtor
,但free
不会。(此外,new
将调用类的ctor
,malloc
不会。)
在您的示例中,char数组显然没有dtor,所以delete只返回内存。这就是为什么它是好的。(反之亦然,新建一个char数组,然后释放它。)
但仍然可能存在一个问题:如果new
和malloc
从不同的堆管理器借用内存怎么办?
想象一下,你从A那里借钱,然后再还给B。即使你对此没意见,你有没有考虑过A的感受?
顺便说一句,您应该使用delete [] pArray;
来释放使用new[]
分配的数组。
free函数需要一个指向malloc分配的内存的指针。编译器只是简单地告诉您没有向它传递指针,这是一个很容易证明的事实。我猜你是想用new创建一个对象,然后在该对象的地址上调用free。但你并没有叫new。
当你纠正这一点时,你很可能会发现免费的成功,但你不应该从任何这样的成功中解读任何重要的东西。你知道这些规则,它们在你的文档中有明确的说明。做你想做的事是不正确的。这种行为是不明确的。从这样的实验中几乎没有什么收获。您将不会了解为什么C内存管理功能不能与C++内存管理功能配对。
你的实验的问题是它没有电。您可以只显示一个特定场景的结果。但这些功能是为全面发挥作用而设计的。你将如何测试每一个可能的场景?
切勿使用试错法编程。您必须始终了解所做操作背后的规则和原理。
您的b
变量不是指针,而是分配给A
的堆栈。如果你想尝试这个实验,那么你会说
A* b = new A;
free(b);
我完全赞成实验,但我不确定你在这里想要实现什么。
在C++中,operator new
不仅分配内存,还运行构造函数。类似地,delete
为您运行析构函数(考虑多态类),并释放返回的内存。另一方面,malloc()
和free()
只是C库例程,(从程序员的角度来看)它们只不过是向操作系统请求一块内存,然后将其交回。
现在,对于一个具有琐碎构造函数和析构函数的简单POD类,可能能够混淆C和C++内存分配,因为没有任何特定于C++的操作。但即使你这样做了,它能证明什么?这是未定义的行为,可能无法在其他编译器上运行。明天可能不行。
如果您使用任何C++特性——继承、虚拟函数等,它肯定不会起作用,因为new
和delete
必须做的工作比分配内存多得多。
所以,请遵守规则!:-)
您没有使用new
创建b
,因此您没有测试预期的假设。(此外,b
的名称不是指针。)
无论如何,malloc
/new
和delete
/free
只能互换工作,只要它们的实现是可互换的。在简单的情况下,在更简单的平台上,这种可能性更大,但从长远来看,这是一种冒险的赌注,或者是一种习惯。
例如,当应用于具有非平凡析构函数的类时,new
的数组形式要求实现(编译器、运行时等)记住数组的大小,以便delete
可以为每个数组对象调用析构函数。除了动态分配的块本身,在new
返回的指针所指的存储器位置之前,几乎没有地方可以放置这样的信息。将该指针传递给free
将需要内存分配器确定分配块的实际开始,这可能是它能够做到的,也可能是它不能做到的
更一般地,new
和malloc
可以指代完全不同的存储器分配器。编译器、运行库和操作系统协同工作,为程序提供内存。切换或升级这些组件中的任何一个都可能导致细节发生变化。
从文档中,
void free (void* ptr);
ptr:
指向以前使用malloc、calloc或realloc。
你传递的不是指针,所以它会抱怨。
A *b = new A();
cout << b->lol() << endl;
free(b); // Will Not cause error
不,不应该混合new/free或malloc/delete,这不是经验法则。
当然,使用free
可以在不崩溃的情况下放弃使用new
获得的分配,但这是高度特定于实现/系统的。
忽略了绕过析构函数或编写会破坏某些系统内存的代码这一事实,更基本、更基本的C++考虑因素实际上很简单:
malloc/free是全局系统函数,而new和delete是运算符。
struct X {
void* operator new(size_t);
void operator delete(void* object);
};
当然,你可以从亚马逊借一本书,然后把它还给你当地的图书馆:一天深夜去那里,把它放在他们前门的台阶上。
它将有未定义的行为,直到并包括你被逮捕——我在这里谈论的是再次混合新的/免费的:)
换句话说:malloc
和free
处理原始内存分配,而new
和delete
处理对象,这是完全不同的。
void* p = new std::vector<int>(100);
void* q = malloc(sizeof(*q));
已经给出了许多好的理由。我还要补充一个原因:new
运算符可以在c++中被覆盖。这允许用户指定他们自己的内存管理模型,如果他们愿意的话。尽管这很少见,但它的存在只是malloc和new、delete和free不能互换使用的又一个原因。这是因为@Patz描述的原因。
free()
接受指针。您正在将对象(而不是指针)传递给free。这就是为什么你会出错。如果您进行以下更改,您的代码工作正常。
A *a = new A;
free(a);
尽管上面的代码运行良好,但它会导致内存泄漏,因为free()
没有调用析构函数,析构函数负责释放构造函数创建的资源。