【问题标题】:using memset to initialize vector of structure which contains array使用 memset 初始化包含数组的结构向量
【发布时间】:2016-05-08 13:45:55
【问题描述】:

我有以下结构

struct a {
     int array[20]
     int array2[45]
}

我已经创建了这个结构的向量

vector<a> vec;

我用过这个 vec。现在我想初始化(设置向量元素中对象内的所有数组值)为零。我该怎么办?

【问题讨论】:

  • 是什么阻止了您阅读std::vector 文档?你为什么决定使用memset?得出这个结论后你做了什么尝试?
  • @LightnessRacesinOrbit:std::vector 通常会为其包含的对象调用默认构造函数。因为这个结构只是内置类型,所以元素不会被零初始化。
  • @MartinBonner:是的,我知道这一点。 OP 需要进行一些研究以找出如何将他的元素归零。这个问题展示了零研究工作。我正在尝试提示 OP进行一些研究
  • 为什么你认为他没有?使用零初始化对象的副本构造向量、为结构提供默认构造函数和 memset 都是可行的方法 - memset 可能是最好的。
  • @MartinBonner std::vector 将对其元素进行值初始化,而不是默认初始化它们。所以它们将被零初始化。您可以通过阅读一些文档来了解这一点。

标签: c++ arrays struct memset


【解决方案1】:

阅读 cmets,您尚未显示填充向量的代码。现在您想重用存储而不是创建一个新存储,并且您想重新初始化它。

任何 C++ 容器的任何算法的规范答案都可能在标准算法中找到。在这种情况下,std::fill。以您喜欢的方式初始化您的结构,并将其复制到您的向量中。沿着这些思路:

a A = {0};
std::fill(vec.begin(), vec.end(), A);

如果您想说这还不够快,请检查一下。我想你会发现它非常有效。上面的代码绝对安全,适用于fill 的任何正确初始化的参数。我怀疑它是否可以在不对a 的实现做出一些假设的情况下做得更快。

【讨论】:

    【解决方案2】:

    事实证明,这是一个比最初看起来更有趣的问题。

    tl;dr:如果您使用的是 C++03 或更高版本的编译器,则无需费心。

    您需要了解value initializationdefault initialization 之间的区别。基本上值初始化会将所有元素设置为零,而默认初始化将使它们完全独立。如果结构的任何默认元素(递归)具有用户定义的默认构造函数,则值和默认初始化都会调用它。

    请注意,值初始化memset为零好,因为

    • 它将调用默认构造函数
    • 它将正确地初始化浮点(到 0.0)和指针(到 NULL)。尽管memset可能会在您的实现中这样做,但不能保证这样做。

    创建带有 n 个元素的向量的正常方法是调用:

    std::vector<a> vec(n);
    

    C++98

    这会调用

    std::vector<a>::vector(size_type count, 
                           const T& value = T(),
                           const Allocator& alloc = Allocator());
    

    value 对象将被默认构造,您需要以某种方式初始化元素。做到这一点的最佳方法是提供要复制的正确值初始化值。所以:

    const static a azeroed;  // Because this is static, it will be value initialized
    std::vector<a> vec(20,azeroed);
    

    技术说明:C++98标准不包含“值初始化”一词,但azeroed的初始化是相同的。

    C++03

    调用了相同的向量构造函数,但从 C++03 开始​​,value 参数值已初始化(所以花园里的一切都是美好的)。

    C++11

    打电话给

    std::vector<a>::vector(size_type count);
    

    哪个值直接初始化元素。

    C++14

    来电

    std::vector<a>::vector(size_type count, const Allocator& alloc = Allocator());
    

    (基本上,他们意识到他们忘记了分配器参数)。这里有一个非常细微的区别,因为元素是通过调用Allocator::construct 构造的,虽然默认分配器会初始化元素,但可以提供一个自定义版本,而不是(参见this answer) .如果你这样做,你几乎肯定知道你在做什么。

    结论

    • 除非你使用的是真正的 C++98 编译器,否则你不需要调用 memset
    • 向向量构造函数提供显式初始化值比调用 memset 更安全。
    • memset 可能无法正确初始化非整数内置值(尽管它可能会)。
    • memset 肯定会破坏任何合适的构造函数。这是一个巨大的维护隐患。如果维护程序员更改结构使其不再是 POD,代码仍将编译 - 它只会做错事。
    • 只需为结构体提供一个适当的默认构造函数,就有很多话要说,这样您就不必担心是否有任何元素已初始化,即使您有一个本地副本。

    【讨论】:

    • 这样做的问题是,如果你改变了类型,可能会使结构不再是 POD,这只会默默地继续将所有内容归零,这可能是一个严重的错误。我发现其中一个曾经在生产中造成多年的间歇性崩溃,使客户损失了数千美元。更喜欢 C++ 中的类型安全解决方案,尤其是在教学时。
    • 这不是一个很好的例子。所示的初始化导致所有元素的初始化数据为零,而无需调用memset
    • @juanchopanza: 章节和诗句好吗?给定结构的定义,默认构造 不会 零初始化它。 (还是我弄错了?)
    • 正如我在另一条评论中所说,std::vector 值初始化元素。
    • @MartinBonner,对于特定的参考,[vector.cons] 表示默认插入的元素。 [container.requirements.general] 将其澄清为allocator_traits&lt;A&gt;::construct(m, p)。从那里,[allocator.traits.members] 将其转换为a.construct(p, std::forward&lt;Args&gt;(args)...) (因为这是默认分配器),[allocator.members] 说这是::new((void *)p) U(std::forward&lt;Args&gt;(args)...),这是当 args 包为空时的值初始化。这有点迂回,并且可以随着分配器而改变,但几乎在所有情况下都应该如此。
    猜你喜欢
    • 2011-03-24
    • 1970-01-01
    • 2020-05-28
    • 2011-08-07
    • 2015-05-04
    • 2018-08-11
    • 2022-12-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多