C++ “有什么用?”;安置新的;?
这里有人用过C++的“新位置”吗?如果是,原因是什么?在我看来,它只在内存映射硬件上有用。如果您正在构建内核,它很有用-您将从磁盘或页面表读取的内核代码放在哪里?你需要知道跳到哪里 或者在其他非常罕见的情况下,例如当您有大量的分配房间,并且希望在彼此后面放置一些结构时。它们可以这样打包,而不需要offsetof()运算符。不过,还有其他一些技巧C++ “有什么用?”;安置新的;?,c++,memory-management,new-operator,placement-new,C++,Memory Management,New Operator,Placement New,这里有人用过C++的“新位置”吗?如果是,原因是什么?在我看来,它只在内存映射硬件上有用。如果您正在构建内核,它很有用-您将从磁盘或页面表读取的内核代码放在哪里?你需要知道跳到哪里 或者在其他非常罕见的情况下,例如当您有大量的分配房间,并且希望在彼此后面放置一些结构时。它们可以这样打包,而不需要offsetof()运算符。不过,还有其他一些技巧 我还相信一些STL实现使用了新的布局,比如std::vector。它们以这种方式为2^n个元素分配空间,并且不需要总是realloc。我用它来存储具有内
我还相信一些STL实现使用了新的布局,比如std::vector。它们以这种方式为2^n个元素分配空间,并且不需要总是realloc。我用它来存储具有内存映射文件的对象。
具体的例子是一个图像数据库,它处理了大量的大图像(超出了内存的容量)。如果您想将分配与初始化分开,这是很有用的。STL使用placement new来创建容器元素。它被
std::vector
使用,因为std::vector
通常分配的内存比vectorplacement new中的对象要多。placement new允许您在内存中构造已分配的对象。
当您需要构造一个对象的多个实例时,您可能希望这样做以进行优化,并且在每次需要新实例时不重新分配内存会更快。相反,对可以容纳多个对象的内存块执行单个分配可能更有效,即使您不想同时使用所有内存块
DevX给出了一个:
标准C++也支持布局
新运算符,它构造
对象位于预先分配的缓冲区上。这
在构建内存池时非常有用,
一个垃圾收集器,或者当
性能和异常安全是重要的
派拉蒙(不存在危险)
由于内存不足,分配失败
已分配,并且
在平面上构造对象
预分配的缓冲区占用的时间更少):
您可能还希望确保在关键代码的特定部分(例如,在起搏器执行的代码中)没有分配失败。在这种情况下,您可能希望更早地分配内存,然后在关键部分中使用placement new
新配售中的解除配售
您不应该取消分配使用内存缓冲区的每个对象。相反,您应该只删除[]原始缓冲区。然后必须手动调用类的析构函数。有关这方面的好建议,请参阅Stroustrup的常见问题解答:?我使用它来构造通过alloca()在堆栈上分配的对象
无耻的插件:我在博客上写过它。我们将它与自定义内存池一起使用。只是一个草图:
class Pool {
public:
Pool() { /* implementation details irrelevant */ };
virtual ~Pool() { /* ditto */ };
virtual void *allocate(size_t);
virtual void deallocate(void *);
static Pool::misc_pool() { return misc_pool_p; /* global MiscPool for general use */ }
};
class ClusterPool : public Pool { /* ... */ };
class FastPool : public Pool { /* ... */ };
class MapPool : public Pool { /* ... */ };
class MiscPool : public Pool { /* ... */ };
// elsewhere...
void *pnew_new(size_t size)
{
return Pool::misc_pool()->allocate(size);
}
void *pnew_new(size_t size, Pool *pool_p)
{
if (!pool_p) {
return Pool::misc_pool()->allocate(size);
}
else {
return pool_p->allocate(size);
}
}
void pnew_delete(void *p)
{
Pool *hp = Pool::find_pool(p);
// note: if p == 0, then Pool::find_pool(p) will return 0.
if (hp) {
hp->deallocate(p);
}
}
// elsewhere...
class Obj {
public:
// misc ctors, dtors, etc.
// just a sampling of new/del operators
void *operator new(size_t s) { return pnew_new(s); }
void *operator new(size_t s, Pool *hp) { return pnew_new(s, hp); }
void operator delete(void *dp) { pnew_delete(dp); }
void operator delete(void *dp, Pool*) { pnew_delete(dp); }
void *operator new[](size_t s) { return pnew_new(s); }
void *operator new[](size_t s, Pool* hp) { return pnew_new(s, hp); }
void operator delete[](void *dp) { pnew_delete(dp); }
void operator delete[](void *dp, Pool*) { pnew_delete(dp); }
};
// elsewhere...
ClusterPool *cp = new ClusterPool(arg1, arg2, ...);
Obj *new_obj = new (cp) Obj(arg_a, arg_b, ...);
现在,您可以在单个内存竞技场中将对象群集在一起,选择一个速度非常快但不释放的分配器,使用内存映射,以及您希望通过选择池并将其作为参数传递给对象的placement new操作符来施加的任何其他语义。我使用它基于包含从网络接收的消息的内存创建对象。通常,placement new用于消除“普通new”的分配成本
我使用它的另一个场景是,我希望能够访问指向尚待构建的对象的指针,以实现每文档单例。我在实时编程中使用过它。我们通常不希望在系统启动后执行任何动态分配(或解除分配),因为无法保证需要多长时间
我所能做的就是预先分配一大块内存(大到足以容纳类可能需要的任何数量的内存)。然后,一旦我在运行时弄清楚如何构造这些东西,就可以使用placement new在我想要的地方构造对象。我知道我使用它的一种情况是帮助创建一个异构系统
这当然不是为胆小的人准备的,但这就是为什么他们把它的语法弄得有点粗糙。我遇到过的一个地方是在容器中分配一个连续的缓冲区,然后根据需要用对象填充它。如前所述,std::vector可能会这样做,我知道MFC CArray和/或CList的一些版本会这样做(因为这是我第一次遇到它的地方)。缓冲区过度分配方法是一种非常有用的优化方法,在这种情况下,放置新对象几乎是构造对象的唯一方法。它有时也用于在直接代码外部分配的内存块中构造对象
我也用过类似的功能,虽然不经常出现。它是C++工具箱的一个有用工具,虽然我曾经见过它用作A(在引擎盖下的部分):
但这里有一个技巧,我用它来获得小类型的快速性能:如果所持有的值可以放入一个void*中,我实际上不需要分配一个新对象,我使用新的位置强制它进入指针本身
脚本引擎可以在本机接口中使用它从脚本分配本机对象。参见Angelscript(www.angelcode.com/Angelscript)获取示例。极客头领:宾果!你完全明白了-这正是它的完美之处。在许多嵌入式环境中,外部约束和/或总体使用场景迫使程序员将对象的分配与其初始化分开。集合在一起,C++调用这个“实例化”;但是,每当构造函数的操作必须在没有动态或自动分配的情况下显式调用时,placement new就是这样做的。它也是找到一个全局C++对象的完美方法,它被固定到硬件组件(内存映射I/O)的地址,或者对于任何静态对象,无论出于何种原因,都必须驻留在固定的位置。
class Pool {
public:
Pool() { /* implementation details irrelevant */ };
virtual ~Pool() { /* ditto */ };
virtual void *allocate(size_t);
virtual void deallocate(void *);
static Pool::misc_pool() { return misc_pool_p; /* global MiscPool for general use */ }
};
class ClusterPool : public Pool { /* ... */ };
class FastPool : public Pool { /* ... */ };
class MapPool : public Pool { /* ... */ };
class MiscPool : public Pool { /* ... */ };
// elsewhere...
void *pnew_new(size_t size)
{
return Pool::misc_pool()->allocate(size);
}
void *pnew_new(size_t size, Pool *pool_p)
{
if (!pool_p) {
return Pool::misc_pool()->allocate(size);
}
else {
return pool_p->allocate(size);
}
}
void pnew_delete(void *p)
{
Pool *hp = Pool::find_pool(p);
// note: if p == 0, then Pool::find_pool(p) will return 0.
if (hp) {
hp->deallocate(p);
}
}
// elsewhere...
class Obj {
public:
// misc ctors, dtors, etc.
// just a sampling of new/del operators
void *operator new(size_t s) { return pnew_new(s); }
void *operator new(size_t s, Pool *hp) { return pnew_new(s, hp); }
void operator delete(void *dp) { pnew_delete(dp); }
void operator delete(void *dp, Pool*) { pnew_delete(dp); }
void *operator new[](size_t s) { return pnew_new(s); }
void *operator new[](size_t s, Pool* hp) { return pnew_new(s, hp); }
void operator delete[](void *dp) { pnew_delete(dp); }
void operator delete[](void *dp, Pool*) { pnew_delete(dp); }
};
// elsewhere...
ClusterPool *cp = new ClusterPool(arg1, arg2, ...);
Obj *new_obj = new (cp) Obj(arg_a, arg_b, ...);
typedef struct _FP
{
unsigned short int rows;
unsigned short int columns;
double array[1]; /* Actually, array[rows][columns] */
} FP;
static Mystruct m;
for(...) {
// re-initialize the structure. Note the use of placement new
// and the extra parenthesis after Mystruct to force initialization.
new (&m) Mystruct();
// do-some work that modifies m's content.
}
vector<Foo> vec;
// Allocate memory for a thousand Foos:
vec.reserve(1000);
Foo* foo = new(free_list.allocate()) Foo(...);
...
foo->~Foo();
free_list.free(foo);
/* Quickly aligns the given pointer to a power of two boundary IN BYTES.
@return An aligned pointer of typename T.
@brief Algorithm is a 2's compliment trick that works by masking off
the desired number in 2's compliment and adding them to the
pointer.
@param pointer The pointer to align.
@param boundary_byte_count The boundary byte count that must be an even
power of 2.
@warning Function does not check if the boundary is a power of 2! */
template <typename T = char>
inline T* AlignUp(void* pointer, uintptr_t boundary_byte_count) {
uintptr_t value = reinterpret_cast<uintptr_t>(pointer);
value += (((~value) + 1) & (boundary_byte_count - 1));
return reinterpret_cast<T*>(value);
}
struct Foo { Foo () {} };
char buffer[sizeof (Foo) + 64];
Foo* foo = new (AlignUp<Foo> (buffer, 64)) Foo ();
error: use of deleted function ‘NonCopyable1& NonCopyable1::operator=(const NonCopyable1&)’
#include <stdio.h>
class NonCopyable1
{
public:
int i = 5;
// Delete the assignment operator to make this class non-copyable
NonCopyable1& operator=(const NonCopyable1& other) = delete;
};
int main()
{
printf("Hello World\n");
NonCopyable1 nc1;
NonCopyable1 nc2;
nc2 = nc1; // copy assignment; compile-time error!
NonCopyable1 nc3 = nc1; // copy constructor; works fine!
return 0;
}
error: use of deleted function ‘NonCopyable1& NonCopyable1::operator=(const NonCopyable1&)’
note: ‘NonCopyable1& NonCopyable1::operator=(const NonCopyable1&)’ is implicitly deleted because the default definition would be ill-formed:
error: non-static const member ‘const int NonCopyable1::i’, can’t use default assignment operator
#include <stdio.h>
class NonCopyable1
{
public:
const int i = 5; // classes with `const` members are non-copyable by default
};
int main()
{
printf("Hello World\n");
NonCopyable1 nc1;
NonCopyable1 nc2;
nc2 = nc1; // copy assignment; compile-time error!
NonCopyable1 nc3 = nc1; // copy constructor; works fine!
return 0;
}
#include <functional>
#include <stdio.h>
class NonCopyable1
{
public:
const int i; // classes with `const` members are non-copyable by default
// Constructor to custom-initialize `i`
NonCopyable1(int val = 5) : i(val)
{
// nothing else to do
}
};
// Some class which (perhaps asynchronously) processes data. You attach a
// callback, which gets called later.
// - Also, this may be a shared library over which you have no or little
// control, so you cannot easily change the prototype of the callable/callback
// function.
class ProcessData
{
public:
void attachCallback(std::function<void(void)> callable)
{
callback_ = callable;
}
void callCallback()
{
callback_();
}
private:
std::function<void(void)> callback_;
};
int main()
{
printf("Hello World\n");
NonCopyable1 outputData; // we need to receive back data through this object
printf("outputData.i (before) = %i\n", outputData.i); // is 5
ProcessData processData;
// Attach a lambda function as a callback, capturing `outputData` by
// reference so we can receive back the data from inside the callback via
// this object even though the callable prototype returns `void` (is a
// `void(void)` callable/function).
processData.attachCallback([&outputData]()
{
int someRandomData = 999;
NonCopyable1 data(someRandomData);
// NOT ALLOWED SINCE COPY OPERATOR (Assignment operator) WAS
// AUTO-DELETED since the class has a `const` data member!
outputData = data;
});
processData.callCallback();
// verify we get 999 here, NOT 5!
printf("outputData.i (after) = %i\n", outputData.i);
return 0;
}
processData.attachCallback([&outputData]()
{
int someRandomData = 999;
NonCopyable1 data(someRandomData);
// NOT ALLOWED SINCE COPY OPERATOR (Assignment operator) WAS
// AUTO-DELETED since the class has a `const` data member!
outputData = data;
});
// (added to top)
#include <cstring> // for `memcpy()`
#include <type_traits> // for `std::is_trivially_copyable<>()`
// Attach a lambda function as a callback, capturing `outputData` by
// reference so we can receive back the data from inside the callback via
// this object even though the callable prototype returns `void` (is a
// `void(void)` callable/function).
processData.attachCallback([&outputData]()
{
int someRandomData = 999;
NonCopyable1 data(someRandomData);
static_assert(std::is_trivially_copyable<NonCopyable1>::value, "NonCopyable1 must "
"be a trivially-copyable type in order to guarantee that `memcpy()` is safe "
"to use on it.");
memcpy(&outputData, &data, sizeof(data));
});
Hello World
outputData.i (before) = 5
outputData.i (after) = 999
processData.attachCallback([&outputData]()
{
int someRandomData = 999;
NonCopyable1 data(someRandomData);
static_assert(std::is_trivially_copyable<NonCopyable1>::value, "NonCopyable1 must "
"be a trivially-copyable type in order to guarantee that `memcpy()` is safe "
"to use on it.");
outputData.~NonCopyable1(); // manually call destructor before overwriting this object
memcpy(&outputData, &data, sizeof(data));
});
// Call`T`'s specified constructor below, constructing it as an object right into
// the memory location pointed to by `ptr_to_buffer`. No dynamic memory allocation
// whatsoever happens at this time. The object `T` is simply constructed into this
// address in memory.
T* ptr_to_T = new(ptr_to_buffer) T(optional_input_args_to_T's_constructor);
// copy-construct `data` right into the address at `&outputData`, using placement new syntax
new(&outputData) NonCopyable1(data);
processData.attachCallback([&outputData]()
{
int someRandomData = 999;
NonCopyable1 data(someRandomData);
outputData.~NonCopyable1(); // manually call destructor before overwriting this object
// copy-construct `data` right into the address at `&outputData`, using placement new syntax
new(&outputData) NonCopyable1(data);
// Assume that `data` will be further manipulated and used below now, but we needed
// its state at this moment in time.
// Note also that under the most trivial of cases, we could have also just called
// out custom constructor right here too, like this. You can call whatever
// constructor you want!
// new(&outputData) NonCopyable1(999);
// ...
});
// Custom copy/assignment operator declaration:
NonCopyable1& operator=(const NonCopyable1& other);
// OR:
// Custom copy/assignment operator definition:
NonCopyable1& operator=(const NonCopyable1& other)
{
// Check for, **and don't allow**, self assignment!
// ie: only copy the contents from the other object
// to this object if it is not the same object (ie: if it is not
// self-assignment)!
if(this != &other)
{
// copy all non-const members manually here, if the class had any; ex:
// j = other.j;
// k = other.k;
// etc.
// Do deep copy of data via any member **pointers**, if such members exist
}
// the assignment function (`operator=()`) expects you to return the
// contents of your own object (the left side), passed by reference, so
// that constructs such as `test1 = test2 = test3;` are valid!
// See this reference, from Stanford, p11, here!:
// http://web.stanford.edu/class/archive/cs/cs106b/cs106b.1084/cs106l/handouts/170_Copy_Constructor_Assignment_Operator.pdf
// MyClass one, two, three;
// three = two = one;
return *this;
}
processData.attachCallback([&outputData]()
{
int someRandomData = 999;
NonCopyable1 data(someRandomData);
static_assert(std::is_trivially_copyable<NonCopyable1>::value, "NonCopyable1 must "
"be a trivially-copyable type in order to guarantee that `memcpy()` is safe "
"to use on it.");
outputData.~NonCopyable1(); // manually call destructor before overwriting this object
memcpy(&outputData, &data, sizeof(data));
});
main.cpp: In lambda function:
main.cpp:151:13: error: static assertion failed: NonCopyable1 must be a trivially-copyable type in order to guarantee that `memcpy()` is safe to use on it.
static_assert(std::is_trivially_copyable<NonCopyable1>::value, "NonCopyable1 must "
^~~~~~~~~~~~~
processData.attachCallback([&outputData]()
{
int someRandomData = 999;
NonCopyable1 data(someRandomData);
outputData.~NonCopyable1(); // manually call destructor before overwriting this object
// copy-construct `data` right into the address at `&outputData`, using placement new syntax
new(&outputData) NonCopyable1(data);
});
// within any scope...
{
char buf[sizeof(T)]; // Statically allocate memory large enough for any object of
// type `T`; it may be misaligned!
// OR, to force proper alignment of your memory buffer for your object of type `T`,
// you may specify memory alignment with `alignas()` like this instead:
alignas(alignof(T)) char buf[sizeof(T)];
T* tptr = new(buf) T; // Construct a `T` object, placing it directly into your
// pre-allocated storage at memory address `buf`.
tptr->~T(); // You must **manually** call the object's destructor.
} // Leaving scope here auto-deallocates your statically-allocated
// memory `buf`.
// This constructs an actual object here, calling the `NonCopyable1` class's
// default constructor.
NonCopyable1 outputData;
// This is just a statically-allocated memory pool. No constructor is called.
// Statically allocate an output buffer properly aligned, and large enough,
// to store 1 single `NonCopyable1` object.
alignas(alignof(NonCopyable1)) uint8_t outputData[sizeof(NonCopyable1)];
NonCopyable1* outputDataPtr = (NonCopyable1*)(&outputData[0]);
NonCopyable1 outputData;
// OR
alignas(alignof(NonCopyable1)) uint8_t outputData[sizeof(NonCopyable1)];
NonCopyable1* outputDataPtr = (NonCopyable1*)(&outputData[0]);
# include <optional>
std::optional<NonCopyable1> outputData = std::nullopt;
processData.attachCallback([&outputData]()
{
int someRandomData = 999;
NonCopyable1 data(someRandomData);
outputData.~NonCopyable1(); // manually call destructor before overwriting this object
// copy-construct `data` right into the address at `&outputData`, using placement new syntax
new(&outputData) NonCopyable1(data);
});
processData.attachCallback([&outputData]()
{
int someRandomData = 999;
NonCopyable1 data(someRandomData);
// emplace `data` right into the `outputData` object
outputData.emplace(data);
});
// verify we get 999 here!
if (outputData.has_value())
{
printf("(*outputData).i (after) = %i\n", (*outputData).i);
// OR
printf("outputData.value().i (after) = %i\n", outputData.value().i);
}
else
{
printf("outputData.has_value() is false!");
}
Hello World
(*outputData).i (after) = 999
outputData.value().i (after) = 999
#include <new>
#include <cstdio>
#include <cstdlib>
int main() {
struct A {
A() {
printf("A()\n");
}
~A() {
printf("~A()\n");
}
char data[1000000000000000000] = {}; // some very big number
};
try {
A *result = new A();
printf("new passed: %p\n", result);
delete result;
} catch (std::bad_alloc) {
printf("new failed\n");
}
}
#include <new>
#include <cstdio>
#include <cstdlib>
int main() {
struct A {
A() {
printf("A()\n");
}
~A() {
printf("~A()\n");
}
char data[1000000000000000000] = {}; // some very big number
};
void *buf = malloc(sizeof(A));
if (buf != nullptr) {
A *result = new(buf) A();
printf("new passed: %p\n", result);
result->~A();
free(result);
} else {
printf("new failed\n");
}
}
#include <cstddef>
#include <cstdio>
int main() {
struct alignas(0x1000) A {
char data[0x1000];
};
printf("max_align_t: %zu\n", alignof(max_align_t));
A a;
printf("a: %p\n", &a);
A *ptr = new A;
printf("ptr: %p\n", ptr);
delete ptr;
}
max_align_t: 16
a: 0x7ffd45e6f000
ptr: 0x1fe3ec0
max_align_t: 16
a: 0x7ffc924f6000
ptr: 0x9f6000
#include <cstddef>
#include <cstdlib>
#include <cstdio>
#include <new>
int main() {
struct alignas(0x1000) A {
char data[0x1000];
};
printf("max_align_t: %zu\n", alignof(max_align_t));
A a;
printf("a: %p\n", &a);
void *buf = aligned_alloc(alignof(A), sizeof(A));
if (buf == nullptr) {
printf("aligned_alloc() failed\n");
exit(1);
}
A *ptr = new(buf) A();
printf("ptr: %p\n", ptr);
ptr->~A();
free(ptr);
}
max_align_t: 16
a: 0x7ffe56b57000
ptr: 0x2416000