用于连续内存的 std::array 与 C 样式数组

Posted

技术标签:

【中文标题】用于连续内存的 std::array 与 C 样式数组【英文标题】:std::array vs C-Style array for contiguous memory 【发布时间】:2018-07-13 12:37:54 【问题描述】:

这是一个仅供兴趣的实验......

我正在尝试制作一个容器,该容器在一个连续的内存块中保存固定数量的字节(例如标头)和动态数据块(例如主体)。在传统的 C 编程中,我会将 char[0] 作为最后一个实例变量,我会过度分配 sizeof(struct) + data_length

这适用于 C++,但我想要更好的东西。所以我的问题是std::array 是以指针开头还是可以像原生 C 样式数组一样使用?

这是一些示例代码...

struct pkg_base

    virtual std::size_t get_body_size() = 0;
    virtual void *get_body_ptr() = 0;
;

template<typename _T, std::size_t _N>
struct pkg
    : public pkg_base

    std::uint16_t a;
    std::uint16_t b;
    std::uint16_t c;

    std::array<_T, _N> body;

    std::size_t get_body_size() override
    
        return ( body.size() );
    

    virtual void *get_body_ptr() override
    
        return ( body.data() );
    
;

void _test_package()

    auto vec = std::vector<std::unique_ptr<pkg_base>>;
    vec.push_back(std::make_unique<pkg<char, 1024>>());
    vec.push_back(std::make_unique<pkg<float, 1024>>());
    vec.push_back( std::make_unique<pkg<std::string, 1024>>() );

    auto const size = vec.front()->get_body_size();
    auto const *ptr = static_cast<char *>( vec.front()->get_body_ptr() );

【问题讨论】:

std::array“以指针开头”是什么意思? “这适用于 C++” - 不,它不支持标准 C++ 不允许零大小的数组。 我不会将std::array 定义为“动态数据块”... placement new 是拥有“可变大小类”的方式。 @oneiros 据我所知,您已将我数字中的撇号删掉,这是为什么呢?这是有效的代码。 【参考方案1】:

所以我的问题是 std::array 是否以指针开头,或者它可以以与本机 C 样式数组相同的方式使用?

来自the documentation

此容器是一种聚合类型,其语义与将 C 样式数组 T[N] 作为其唯一非静态数据成员的结构相同。

所以那里没有其他数据成员,只有您想要的 T[N] 数组。

您可以使用sizeof 或查看代码自行确认。

顺便说一句,以_[A-Z] 开头的名称是为实现保留的,因此您可能不应该调用模板类型参数_T_N

【讨论】:

【参考方案2】:

std::array 不包含任何指向其他地方数据的指针,std::array 直接在内部保存数据,其中没有任何动态。

它旨在提供类似于标准数组的语义,但具有标准 STL 容器的一些特征。

这是一个聚合类,主要实现为

namespace std

  template<typename T, size_t S>
  class array
  
    T __elems_[S];
  

你有 array.data() 已经返回一个 C 数组,所以我不明白你的要求。

【讨论】:

【参考方案3】:

作为参考,您所指的技术称为flexible array member,不幸的是,C++ 不支持它作为核心功能或标准库函数。我觉得这令人失望。

std::array 是一个修饰过的 C 样式数组(有一些成员允许它用作 STL 容器,如迭代、调整大小、类型自省等)。

我知道实现类似于灵活数组成员的功能的唯一方法是创建一个std::vector&lt;char&gt;,其大小设置为sizeof(header) + &lt;extra bytes for payload&gt;,而不是vector.data() 中的placement new 标头。您可以将所有这些包装在一个帮助类中,以便为多种场景节省一些输入。

【讨论】:

【参考方案4】:

我可以建议将类型擦除作为您问题的一般解决方案。在现代 C++ 中,除非不可避免,否则倾向于远离直接操作原始指针:

struct common_part
    //declare common data and ctor
protected:
    virtual elem* end()return begin()+size();;
    virtual elem *begin()=0 ;
    virtual ~common_part()=default;
    virtual std::size_t size()=0;
;

template<std::size_t N>
struct instance_type:
    common_part
protected:
    void elem* begin() overridereturn arr;;
    void elem* end() overridereturn arr+N;;
    void std::size_t size() overridereturn N;;
private:
    elem arr[N];
;

std::unique_ptr<common_part> ptr new instance_type<N>;

【讨论】:

当您不需要多态性时,为什么还要virtual 成员?为此目的,模板绰绰有余。 为了类型擦除。否则,pairbegin,end 必须声明为公共数据成员并进行特殊处理。运行时多态性是一种更灵活、健壮但紧凑且经济的方法。在代码膨胀的情况下,begin,end 的数据成员看起来更好,但最后 dtor 应该是虚拟的,以便在处理时由指针选择正确的 dtor。

以上是关于用于连续内存的 std::array 与 C 样式数组的主要内容,如果未能解决你的问题,请参考以下文章

在 C++11 中具有对齐元素的 std::array 类型

Leetcode刷题笔记-数组

数组向量的内存布局是啥?

C++ STL应用与实现5: 如何使用std::array (since C++11)

std::vector<std::array<T, N>> 或 std::array<std::vector<T>,N> 类型的数组如何存储在内存中?

从 std::array 获取对原始数组的引用