我对某些事感到困惑.假设我有一个任意的C++分配器 - 比方说,像这样:
template<class T>
struct my_allocator
{
template<class Other>
struct rebind { typedef my_allocator<Other> other; };
// [other members here]
};
Run Code Online (Sandbox Code Playgroud)
现在考虑以下代码(请阅读评论):
typedef my_allocator<int> Alloc;
Alloc alloc = get_my_allocator(); // assume this works properly
long *const p = Alloc::rebind<long>::other(alloc).allocate(1, NULL);
// Notice that the rebound allocator for 'long' is now destroyed
// Can a NEW rebound allocator for 'long' deallocate the memory from the old one?
Alloc::rebind<long>::other(alloc).deallocate(p, 1);
// i.e., does the 'int' allocator 'alloc' keep alive the 'long' …Run Code Online (Sandbox Code Playgroud) 作为这个问题的后续,默认的allocator(std::allocator<T>)需要construct按如下方式实现(根据[default.allocator]):
Run Code Online (Sandbox Code Playgroud)template <class U, class... Args> void construct(U* p, Args&&... args);效果:
::new((void *)p) U(std::forward<Args>(args)...)
也就是说,始终是值初始化.结果是std::vector<POD> v(num),对于任何pod类型,将值初始化num元素 - 这比默认初始化num元素更昂贵.
为什么没有† std::allocator提供默认初始化的额外过载?就是这样的(借用凯西):
template <class U>
void construct(U* p) noexcept(std::is_nothrow_default_constructible<U>::value)
{
::new(static_cast<void*>(p)) U;
}
Run Code Online (Sandbox Code Playgroud)
是否有理由在呼叫案例中更喜欢值初始化?对我来说这似乎令人惊讶,这打破了通常的C++规则,我们只支付我们想要使用的内容.
†我认为这样的改变是不可能的,因为目前std::vector<int> v(100)会给你100 0秒,但我想知道为什么会这样......因为人们可以很容易地std::vector<int> v2(100, 0)以同样的方式要求有差异之间new int[100]和new int[100]{}.
在C++标准库中,std::string有一个公共成员函数capacity(),它返回内部分配存储的大小,一个大于或等于字符串中字符数的值(根据此处).这个值可以用于什么?它与自定义分配器有关吗?
为什么不是标准的C++ 03接口来查询C++ 0x中使用的分配器的成员类型?成员类型不足的用例有哪些?
我注意到c ++中的allocator为void类型提供了特化.这样做有什么特别的目的吗?为void类型分配内存没有意义,对吧?
std::scoped_allocator_adaptor到目前为止,在尝试使用gcc 4.7.0中实现的C++ 11时,我注意到C++ 11 FDIS定义了std::uses_allocatorfor tuples(20.4.2.8[tuple.traits])的特化,但是没有定义对,尽管出于所有其他目的,对看起来和行为就像元组(他们的专长std::get,std::tuple_size等等).
在进一步阅读中,介绍这些东西的N2554也定义了对的allocator_arg构造函数和特化uses_allocator(第23-24页).
他们为什么要成对掉队?有没有其他方法可以使用我看不到的,或者这是否有一些赞成元组的对折旧?
我的测试代码是:
// myalloc is like std::allocator, but has a single-argument
// constructor that takes an int, and has NO default constructor
typedef std::vector<int, myalloc<int>> innervector_t;
typedef std::tuple<int, innervector_t> elem_t;
typedef std::scoped_allocator_adaptor<myalloc<elem_t>, myalloc<int>> Alloc;
Alloc a(1,2);
std::vector<elem_t, Alloc> v(a);
v.resize(1); // uses allocator #1 for elements of v
// the following line fails to compile if pair is …Run Code Online (Sandbox Code Playgroud) 我是C++的新手,我在项目中使用了矢量类.我发现它非常有用,因为我可以有一个数组,只要有必要就会自动重新分配(例如,如果我想推送一个项目并且向量已达到它的最大容量,它会重新分配自己,为操作系统提供更多的内存空间),所以对向量元素的访问非常快(它不像列表,要达到"第n"元素,我必须通过"n"个第一个元素).
我发现这个问题非常有用,因为当我想将我的向量存储在堆/堆栈上时,他们的答案完全解释了"内存分配器"是如何工作的:
[1] vector<Type> vect;
[2] vector<Type> *vect = new vector<Type>;
[3] vector<Type*> vect;
Run Code Online (Sandbox Code Playgroud)
然而,怀疑是困扰我一段时间,我找不到它的答案:每当我构建一个向量并开始推入大量项目时,它将达到向量将满的时刻,因此继续增长它需要重新分配,将自身复制到一个新位置,然后继续push_back项(显然,这个重新分配它隐藏在类的实现上,所以它对我来说是完全透明的)
好吧,如果我在堆上创建了向量[2],我没有想象可能发生的事情:类向量调用malloc,获取新空间然后将自身复制到新内存中,最后删除调用free的旧内存.
然而,当我在堆栈上构造一个向量时,面纱会隐藏正在发生的事情[1]:当向量必须重新分配时会发生什么?AFAIK,无论何时在C/C++上输入一个新函数,计算机都会查看变量声明,然后展开堆栈以获得放置这些变量所需的空间,但是当你在堆栈中分配更多空间时功能已在运行.类向量如何解决这个问题?
8年前,Stephen Lavavej发表了这篇博客文章,其中包含一个名为"Mallocator"的简单分配器实现.从那时起,我们已经过渡到C++ 11时代(很快就会出现C++ 17)......新的语言特征和规则是否会影响Mallocator,或者它仍然是相关的?
我想将 astd::pmr::unordered_map与 a一起使用std::pmr::monotonic_buffer_resource。两者配合得很好,因为集合的节点是稳定的,所以我不会通过重新分配在缓冲区资源中创建很多漏洞:
std::pmr::monotonic_buffer_resource res;
std::pmr::unordered_set<T> set(&res);
Run Code Online (Sandbox Code Playgroud)
也就是说,除了桶列表,当集合超过max_load_factor(). 假设我无法reserve()摆脱这种情况,并且我实际上关心自增长以来旧桶列表留下的缓冲区资源中的漏洞,我有什么选择?
如果我知道它unordered_set是作为 实现的std::vector<std::forward_list>,就像在(某些版本的)MSVC 中一样,那么我应该能够使用 ascoped_allocator为vector和forward_list. 但)我不能依赖unordered_set是一个vector<forward_list>可移植的代码和b)scoped_allocator是一个Allocator,而monotonic_buffer_resource是memory_resource,阻抗失配,这将使非常复杂的初始化。
或者我可以根据请求的大小编写一个switch_memory_resource委托给其他memory_resources 的 s 。然后,我可以使用monotonic_buffer_resource与节点大小匹配的请求(但是,我不能,可移植地,知道)以及default_memory_resource()其他所有内容。我可能会做出有根据的猜测,节点最多为sizeof(struct {void* next; size_t hash; T value;}),通过将其乘以 2 来添加一些误差幅度,并将其用作两个memory_resources之间的截止点,但我想知道是否有更简洁的方法?
我正在努力解决自定义分配器的性能问题。我的问题是关于调试版本。
通常情况下,如果只有一点点下降我并不介意。但目前我正在以 4fps 播放某些内容,而如果没有自定义分配器,则播放速度为 60fps(并且可能会更快)。这使得软件开发变得更加困难。
我一直把它确定下来......基本上继承了标准分配器
请参阅“quick-bench.com”的以下结果 https://quick-bench.com/q/ep3uyYNK6rh_6f8AGAP0zIAflAA
蓝色条很简单:
int main() {
std::vector<uint8_t, std::vector<uint8_t>::allocator_type> buffer;
buffer.reserve(numBytes);
buffer.resize(numBytes);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
黄色条:
template<typename T>
class CustomAllocatorType : public std::vector<uint8_t>::allocator_type {};
int main() {
std::vector<uint8_t, CustomAllocatorType<uint8_t>> buffer;
buffer.reserve(numBytes);
buffer.resize(numBytes);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
用以下内容封装自定义分配器:
#pragma GCC push_options
#pragma GCC optimize ("-O3")
// ....
#pragma GCC pop_options
Run Code Online (Sandbox Code Playgroud)
没有任何效果。我想我需要对向量实例本身执行此操作,但我不想走那么远......
有谁知道这个问题的解决方案?