我将首先用c++发布我的测试程序:
#include <iostream>
using namespace std;
class X
{
int x;
public:
X()
{
cout<<"constructingn";
x=0;
}
int& getx()
{
return x;
}
~X()
{
cout<<"destroyingn";
}
};
int main()
{
X* p=(X*)malloc(sizeof(X));
++p->getx();
p->getx()*=5;
cout<<p->getx();
free(p);
return 0;
}
输出:5
现在,在有人抱怨为什么我使用malloc
&free
在c++程序中,我想重申,它只是一个测试程序,即使使用operator new
&operator delete
。但我的问题仍然是:
- 即使没有使用
malloc or operator new
创建X的对象,我们如何访问类X的变量X ? - 显然
free & operator delete
也不会破坏对象并执行仅仅的分配。如果我用new
创建一个对象,但使用operator delete or free
而不是delete
,会发生什么?我的对象还会在那里吗?它还能用吗?
-
如果您通过调用
free()
来释放使用new
创建的对象,您将陷入未定义行为。同样,如果用delete
释放一个malloc()
'ed对象,就会有未定义的行为。无论你做什么,永远不要把这两者混在一起。 -
malloc()
的语义与new
不同:malloc()
只是分配内存,它不调用构造函数。new
执行分配,并调用相应的构造函数。同样,
free()
和delete
之间也有类似的区别:delete
在释放内存之前调用析构函数,free()
不这样做。
你可以在c++中使用malloc()
来支持一个真正的c++对象,但是你必须自己做构造函数/析构函数调用:
//The placement-new below needs this:
#include <new>
//This is what new does:
char* temp = (char*)malloc(sizeof(Foo)); //allocation only
Foo* foo = new(temp) Foo(); //placement-new: construction only
//This is what delete does:
foo->~Foo(); //destruction only
free((void*)foo); //deallocation only
注意,第二行中的place -new语法是在c++中显式调用构造函数的唯一方法。析构函数可以像其他成员一样显式调用。造成这种不对称的原因是,对象在销毁之前确实是一个有效对象,而在构造之前则不是。
对于你关于p->getx()
为什么编译的问题。这可以归结为代码中的这个转换:
X* p=(X*)malloc(sizeof(X));
^
|
this little cast
在这里,程序员明确地告诉编译器:"我知道我给你的值看起来不像指向X
的指针,但我告诉你它是。"所以,愚蠢的编译器,不要再提类型不匹配了,把它当作指向X
的指针,因为我,人类,你的上帝,告诉你了!"
编译器能做什么?它关闭了类型不匹配,并将指针视为指向X
的指针,正如您告诉它的那样。如果那不对,那是你的问题。也许程序会正常运行,也许它会崩溃,也许它会悄悄地破坏数据,也许会出现一个粉红色的大象。你的编译器不会在意的。它唯一关心的是它实现了你的愿望。编译器可以非常顺从。
回答问题的这一部分:"如果我用new创建对象,但使用delete或free操作符而不是delete,会发生什么?"我的对象还会在那里吗?它还能用吗?"
用malloc
分配和用delete
释放是未定义的行为。在调用对象的析构函数后,不能保证实现将使用C的free
。
反之亦然。如果使用new
进行分配,则不能保证返回的指针来自内部调用的malloc
、realloc
或calloc
,只有在将指针传递给free
是安全的情况下才可以。
即使它可以工作,它也可能破坏程序和/或泄漏资源,因为您将跳过对象的构造函数或析构函数。
编辑:你说"显然免费&操作符删除也不破坏对象,只执行错位。嗯,这是错误的。delete
将调用对象的析构函数,因此它将先销毁,然后再释放。
至于评论中澄清的问题,对于"为什么你仍然可以访问x",好吧,malloc
将分配对象占用的全部空间(在你的情况下,我相信只是x
变量),变量将在那里,只有构造函数不会被调用,所以,它的值不会被设置为零。如果在运行程序时初始值为0,那么这只是一个巧合。
读了这么多的观点,我自己写了一个程序来解释你的问题。-
#include <iostream>
#include <vector>
using namespace std;
class X
{
int x;
vector<int> v;
public:
X()
{
cout<<"constructingn";
x=0;
v.push_back(1);
v.push_back(2);
}
int& getx()
{
return x;
}
vector<int>& getv()
{
return v;
}
~X()
{
cout<<"destroyingn";
}
};
int main()
{
X* p=(X*)operator new(sizeof(X));
++p->getx();
p->getx()*=5;
cout<<p->getx()<<"n";
for (int x:p->getv())
cout<<x<<" ";
cout<<"nexecutedn";
operator delete(p);
return 0;
}
/* Output :-
5
executed
*/
看看p
是如何忽略向量v
而选择行executed
的。这是因为vector<int>
不是operator new (or malloc in your case)
创建的class
(或者更准确地说是class template
)。您的程序显示了x
的输出,因为它是基本类型&不是一个类。对于类,你需要一个构造函数&因此operator new or malloc
不适合类&这就是输出。如果您简单地替换operator new with new
&operator delete with delete
则输出将是:-
constructing
5
1 2
executed
destroying
现在你的代码给出了正确的&好结果!刚果!
对于你的第二个问题,永远不要把malloc & free
和new & delete
混在一起,因为它会创建UB,而不会产生这样的结果。