在 C++ 中,为什么需要“new”来动态创建对象而不仅仅是分配?
我有这个简单的类层次结构:
class Base {
public:
virtual int x( ) const = 0;
};
class Derived : public Base {
int _x;
public:
Derived( int x ) : _x(x) { }
int x( ) const { return _x; }
};
如果我使用 malloc
分配 Derived
的实例,然后尝试访问多态函数 x
,程序崩溃(我遇到分段错误):
int main( ) {
Derived *d;
d = (Derived*) malloc( sizeof(Derived) );
*d = Derived( 123 );
std::cout << d->x() << std::endl; // crash
return 0;
}
当然,我的实际应用程序要复杂得多(它是一种内存池)。
我很确定这是因为我分配d
的方式:我没有使用new
。
我知道展示位置新
运算符,这一定是我需要的,但我从未使用过它,并且有一些问题:
-
如果我不使用
new
,为什么我的应用程序会崩溃?new
实际上是做什么的?为什么我不能直接使用赋值运算符将
Derived( 123 );
的值赋给d
指向的内存区域? -
对于非多态类型,我是否也需要使用
new
?POD 怎么样?
-
在我上面链接的C++常见问题 它表示传递给放置
new
的内存区域必须与我正在创建的对象对齐。我知道什么是对齐,但我不知道如何检查我的类所需的对齐。
<块引用>malloc
手册说:malloc() 和 calloc() 函数返回一个指向已分配内存的指针,该指针适合任何类型的变量。
我希望我的类所需的对齐方式是
sizeof
返回的类大小,以便address_returned_by_malloc + i * sizeof(my_class)
形式的任何地址> 适合分配我的对象。我的希望对吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
让我们继续往下看,
虚拟表已损坏。
虚拟表卡在分配的内存之后。当您
new
一个类时,生成的代码将正确设置vtable。但是,malloc 不会正确初始化 vtable要查看虚拟表,请运行
g++ -fdump-class-hierarchy
出于类似的原因,在不重载operator=的情况下,生成的汇编代码将只复制数据而不复制vtable[再次强调,编译器只知道复制数据,而不知道复制vtable]
如果你想查看带有有效 vtable 函数的基于指针的版本:
如果您使用的是虚函数,那么是的,即使对于非多态类型,
对齐不是问题。
Let's go down the line
Virtual table is corrupted.
The virtual table is stuck right after the allocated memory. when you
new
a class, the generated code will properly set up the vtable. However, malloc will not properly initialize the vtableTo see the virtual table, run
g++ -fdump-class-hierarchy
For a similar reason, without overloading operator=, the generated assembly code will only copy the data and not the vtable [again, the compiler only knows to copy the data, not the vtable]
If you want to see a pointer-based version with a valid vtable function:
If you are using virtual functions, then yes, even for non-polymorphic types
Alignment is not an issue.
因为
malloc
不会调用类的构造函数,并且不知道它可能具有的任何特定对齐要求。如果您需要使用malloc
(不推荐),请查看placement new(假设您出于某种原因不想超载常规的new
)。Because
malloc
doesn't call the class's constructor, and doesn't know anything about any particular alignment requirements it might have. If you need to usemalloc
(not recommended), take a look at placement new (assuming you don't want to overload the regularnew
for some reason).我不相信使用 malloc 时会调用对象的构造函数。
I don't belive that the object's constructor is called when you use malloc.
标准的
[basic.life]
部分说由于您的类具有虚拟成员,因此需要进行重要的初始化。您无法分配生命周期尚未开始的对象,必须使用
new
初始化它。section
[basic.life]
of the standard saysSince your class has virtual members, it requires non-trivial initialization. You can't assign an object whose lifetime hasn't started, you have to initialize it with
new
.具有虚拟成员的类包含一个指向所谓的 vtable 的指针 - 基本上是指向这些虚拟成员的实现的函数指针表。当您使用
operator new
时,会调用构造函数,即使它是隐式构造函数,也会正确设置指向虚函数表的指针。但是,malloc 不会调用构造函数。 vtable指针未初始化,指向一些随机内存。当您尝试调用虚拟函数时,您会取消引用错误的指针并崩溃(未定义的行为)。
解决方案是在使用对象之前使用placement new 来初始化该对象:
需要注意的一些重要事项:
=
进行分配没有帮助。=
的默认实现会复制所有成员变量,但 vtable 指针不是成员,不会被复制。Classes with
virtual
members contain a pointer to a so-called vtable - basically a table of function pointers to the implementation of these virtual members. When you useoperator new
, the constructor is called, which, even if it is an implicit constructor, will set up this pointer to the vtable properly.However, malloc does not call the constructor. The vtable pointer is left uninitialized, point to some random memory. When you then attempt to call a virtual function, you dereference a bad pointer and crash (undefined behavior).
The solution is to use placement new to initialize the object before using it:
Some important things to note:
=
does not help. The default implementation of=
copies all member variables, but the vtable pointer is not a member and is not copied.