用于连续内存的 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<char>
,其大小设置为sizeof(header) + <extra bytes for payload>
,而不是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 类型
C++ STL应用与实现5: 如何使用std::array (since C++11)
std::vector<std::array<T, N>> 或 std::array<std::vector<T>,N> 类型的数组如何存储在内存中?