【问题标题】:Unable to Understand Custom Allocator无法理解自定义分配器
【发布时间】:2021-06-08 01:56:04
【问题描述】:

我在 Microsoft 文档网站上阅读了有关 typedefs 与 using 的信息:Aliases and typedefs (C++)

#include <stdlib.h>
#include <new>

template <typename T> struct MyAlloc {
    typedef T value_type; // Failed to understand why it is needed

    MyAlloc() { }
    template <typename U> MyAlloc(const MyAlloc<U>&) { } // Failed to understand why it is needed

    bool operator==(const MyAlloc&) const { return true; } // Failed to understand why always true
    bool operator!=(const MyAlloc&) const { return false; } // Failed to understand why always false

    T * allocate(const size_t n) const {
        if (n == 0) {
            return nullptr;
        }

        if (n > static_cast<size_t>(-1) / sizeof(T)) // Failed to understand this operation
        { 
            throw std::bad_array_new_length();
        }

        void * const pv = malloc(n * sizeof(T));

        if (!pv) {
            throw std::bad_alloc();
        }

        return static_cast<T *>(pv);
    }

    void deallocate(T * const p, size_t) const {
        free(p);
    }
};

#include <vector>
using MyIntVector = std::vector<int, MyAlloc<int>>;

#include <iostream>

int main ()
{
    MyIntVector foov = { 1701, 1764, 1664 };

    for (auto a: foov) std::cout << a << " ";
    std::cout << "\n";

    return 0;
}

正如我在整个代码中评论的那样,我在很多地方都无法理解这段代码。有人可以为 C++ 初级到中级水平的人解释上述代码吗?

【问题讨论】:

  • StackOverflow 不是一个教程网站。您对代码到底有什么不明白的地方?请提出具体问题。
  • 您的班级必须满足某些标准才能成为合适的分配器。请参阅 cppreference 上的 Allocator 要求,了解您的分配器必须提供的确切内容。
  • 请在每个帖子中提出一个具体问题,我在这篇帖子中看到至少 4 个单独的问题。

标签: c++ c++11


【解决方案1】:

基本上,所有问题的答案是,如果您想拥有满足Allocator 要求的类型,标准要求它们。

typedef T value_type;

value_type 是每个分配器的必需成员类型。标准要求分配器使用它,因为这是从该类型或对象中提取 T 的唯一方法。我真的想不出分配器有什么用处,否则这种类型将不可用,但它仍然没有成本,并且与其他标准类型一致。

template <typename U> MyAlloc(const MyAlloc<U>&) { } // Failed to understand why it is needed

这是一个重新绑定的复制构造函数。我不知道为什么需要这样做,但它可以用于例如将 MyAlloc&lt;SomeCustomType&gt; 转换为 MyAlloc&lt;char&gt; 以获得更具体的内存放置(例如,忽略对象之间的一些填充)。

bool operator==(const MyAlloc&) const { return true; } // Failed to understand why always true
bool operator!=(const MyAlloc&) const { return false; } // Failed to understand why always false

分配器的等式运算符应该返回

true 仅当分配器a1 分配的存储可以通过a2 释放。

分配器本身并不管理内存,因此它们是可替换的。只要您可以互换使用不同的对象,您应该返回true。它们不能互换使用的情况是,例如,分配器在构造函数中接受一个参数,告诉它从不同的内存池分配 - 在这种情况下,您应该检查其他对象是否真的可以释放您分配的内存。

if (n > static_cast<size_t>(-1) / sizeof(T)) // Failed to understand this operation

size_t 是无符号类型,保证适合任何对象的最大sizeof。将 -1 转换为无符号类型会导致将其转换为该类型可表示的最大数字 (0xFFFF(...))。

然后你将它除以要分配的类型的大小,这给你可以分配的对象的绝对最大值。实际最大值要低得多,因为程序本身需要一些内存(堆栈、全局区域等)。

如果有人请求超过这个绝对最大值,那么这个请求永远不会被这台机器上的这个程序满足,标准对此有一个更具体的例外,而不是简单的“内存不足”。这不是必需的,但可以很好地通知用户他们确实无法在 8GB 机器上请求 1TB 内存。

【讨论】:

  • “分配器自己不管理内存”——绝对允许。您引用标准的相关部分,分配器类应该像那样实现它。在这种情况下,对于任何一对分配器a1,a2,分配器a2 可以释放由a1 分配的内存,因为这个分配器总是转发到::free
【解决方案2】:

主要与制作新分配器的要求有关。请参阅此页面以获取所需要求的总列表https://en.cppreference.com/w/cpp/named_req/Allocator 请注意,并非所有要求都需要满足。我不确定您如何知道可以忽略哪些要求。

  • value_type 会员:见要求

  • !=== 运算符:请参阅要求,请注意页面说明比较两个分配器实例 a1a2 的比较运算符应该只返回 true 如果由 a1 分配的内存可以通过a2解除分配

  • n &gt; static_cast&lt;size_t&gt;(-1) / sizeof(T): 这个表达式有一些棘手的地方,让我们把它分解成它的组件。

  • sizeof(T) 是正在分配的对象的大小,以字节为单位

  • n 是请求分配的对象数

  • static_cast&lt;size_t&gt;(-1) 保证评估为最大可能的size_t 值(请参阅How portable is casting -1 to an unsigned type?)。此外,size_t 保证以字节为单位存储最大可能大小的对象 (https://en.cppreference.com/w/cpp/types/size_t)。

综上所述,这行代码验证了分配大小为Tn 对象不会导致表达式(n * sizeof(T)) 溢出size_t 类型并给malloc 提供一个意外的参数。

【讨论】:

    猜你喜欢
    • 2015-05-29
    • 2015-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-02
    • 1970-01-01
    • 2011-04-24
    相关资源
    最近更新 更多