C++ Sytanx 关于类数组和新运算符的使用

C++ Sytanx about Array of Class and usage of new operator

本文关键字:运算符 Sytanx 于类 数组 C++      更新时间:2023-10-16

我的问题是关于斯科特·迈耶(Scott Meyer)的书"更有效C++35种新方法......"中的代码快照。

代码(参数名称已更改)

void * memory = operator new[] (10*sizeOf(MyClass));
MyClass * myArray = static_cast<MyClass*>(memory);
for(int i= 0; i<10; i++)
{
new (&myArray[i]) MyClass(params);
}

我不熟悉这种语法。甚至运算符 new [] 和新的 (&myArray[i]) ...是否有任何资源可以阅读有关该语法的详细信息,它们是如何工作的。

要理解这一点,您首先需要了解简单明了new的工作原理。new表达式的常用语法类似于new T。使用new表达式时,将发生以下情况:

  1. 首先,它调用分配函数来获取对象的存储。分配函数只需返回一个指针,指向某个足够大的已分配存储以适合请求的对象。它的作用仅此而已。它不初始化对象。

  2. 接下来,在分配的空间中初始化对象。

  3. 返回指向已分配空间(以及现在已初始化的对象)的指针。

new T的情况下,分配函数被命名为operator new。分配数组时,例如new T[5],分配函数被命名为operator new[]。这些函数的默认定义在全局命名空间中提供。它们各自采用std::size_t类型的单个参数,即所需的字节数。所以当你做new T时,对应的调用是operator new(sizeof(T)),而对于new T[5]operator new[](sizeof(T)*5)被调用。

但是,可以将更多参数传递给分配函数。这称为放置新语法。要传递更多参数,请使用如下语法:new (some, arguments, 3) T,这将调用operator new(sizeof(T), some, arguments, 3)。参数列表位于new和类型之间的括号中。

除了实现提供的简单operator new(std::size_t)及其数组对应项之外,还有一些默认定义采用void*类型的额外参数。也就是说,它们获取指向对象的指针。这些函数实际上不会分配任何空间,而只是返回您提供给它们的指针。所以当你做new (some_pointer) T时,它首先调用operator new(sizeof(T), some_pointer),它只是再次返回some_pointer,然后初始化该空间中的对象。这为您提供了一种在某个已分配的空间中初始化对象的方法。

所以现在我们有这四个预定义的分配函数(事实上,还有其他一些,你也可以自由定义自己的):

// Normal allocation functions that allocate space of the given size
operator new(std::size_t)
operator new[](std::size_t)
// Placement allocation functions that just return the pointer they're given
operator new(std::size_t, void*)
operator new[](std::size_t, void*)

因此,让我们看一下您提供的代码片段:

void * memory = operator new[] (10*sizeOf(MyClass));
MyClass * myArray = static_cast<MyClass*>(memory);
for(int i= 0; i<10; i++)
{
new (&myArray[i]) MyClass(params);
}

在第一行中,我们直接调用operator new[]来分配一些存储。多少存储空间?好吧,足够 10 个MyClass类型的对象.此函数返回指向该分配存储的指针,并将其存储在memory中。

在此之后,void*被强制转换为MyClass*,以允许您以大小为sizeof(MyClass)的块索引分配的存储,即myArray[0]现在将指向第一个MyClassmyArray[1]指向第二个。

现在我们遍历该数组,使用分配存储的每个未初始化的MyClass大小位的地址调用放置 new。例如,在第一次迭代中,这将调用分配函数operator new(sizeof(MyClass), &myArray[0])正如我们之前看到的,它除了返回你给它的指针外什么都不做。新表达式将通过初始化此空间中的MyClass对象并返回指向该对象的指针来完成。

因此,总而言之,代码分配一些存储以适应 10 个MyClass对象(但不初始化它们),然后循环遍历该存储中的每个MyClass大小的空间,初始化每个对象中的对象。

这演示了如何在已预分配的存储中初始化对象。您可以对新初始化的对象一遍又一遍地重复使用相同的存储。

维基百科将回答您的问题。

第一个new用于分配原始内存。

http://en.wikipedia.org/wiki/New_(C%2B%2B)#void.2A_operator_new.28size_t_size.29

仅分配内存的C++语言结构称为 void* 运算符 new(size_t 大小)。它由新人在分配阶段使用。可以按类重写它以定义特定于类的内存分配器。


第二个new称为放置新。

http://en.wikipedia.org/wiki/Placement_new

使用附加 void * 参数的运算符 new 和运算符删除的放置重载用于默认放置,也称为指针放置。

void * memory = operator new[] (10*sizeOf(MyClass));

在这里,您可以分配大小为10*sizeOf(MyClass)字节的内存。这是原始未初始化的内存。此内存中没有构造C++对象。

new (&myArray[i]) MyClass(params);

在这里,您使用placement-new&myArray[i]指向的给定内存中构造一个对象。

放置-new 的典型语法是这样的:

X * x = new (pAllocatedMem) X(a,b,c);

这意味着,你构造一个类型为X的对象,将abc传递给构造函数。该对象是在pAllocatedMem指向的内存中构造的。

另请注意,您删除使用放置新构造的此类对象,例如:

x->~X(); //delete the constructed object. DONT USE : delete x;

也就是说,您无需执行delete x即可删除此类对象。

new (&myArray[i]) MyClass(params);

放置new运算符。它允许您在预先分配的内存位置创建对象。

18.4.1.3 放置表格

void* 运算符 new(std::size_t size, void* ptr) throw();

返回: ptr.

3 注意:有意不执行其他操作.
4 [示例:这对于在已知地址构造对象很有用:

void* place = operator new(sizeof(Something));
Something* p = new (place) Something();
—end example]
void* operator new[](std::size_t size, void* ptr) throw();
5 Returns: ptr.
6 Notes: Intentionally performs no other action.