初始化 std::vector 的 std::string

11

在与一些遗留代码一起工作的项目中,我发现了这个函数:

std::vector<std::string> Object::getTypes(){
    static std::string types [] = {"type1","type2", "type3"};
    return std::vector<std::string> (types , types +2);
}

我可能会把它写成:

std::vector<std::string> Object::getTypes(){
    std::vector<std::string> types;
    types.push_back("type1");
    types.push_back("type2");
    types.push_back("type3");
    return types;
}

这只是一种风格选择还是我忽略了什么?任何帮助都将不胜感激。如果这太基础了,对不起。

更新: 实际上找到了不同的类覆盖同一个方法的方式,因此更加模糊不清。我会使它们全部变成相同的,但如果有更好的方法,我更愿意采用更好的方法。


编辑

请注意,上述旧代码是不正确的,因为它仅使用数组的前两个元素进行初始化。然而,这个错误已经在评论中讨论过,因此应该保留。

正确的初始化应该如下所示:

...
    return std::vector<std::string> (types, types + 3);
...

2
第一次咒语原则上可能更有效,因为它不涉及任何内存重新分配。还有较少的函数调用。请注意,在C++11中,您可以说return std::vector<std::string>{"type1","type2", "type3"}; - juanchopanza
2
@juanchopanza,此外,在那个版本中,代码和数据的隔离更加明显,因此语法更加清晰。 - user529758
1
第一个版本更容易出错,这在这里的偏移错误中得到了证明。 - Mike Seymour
@MikeSeymour 如果你使用 std::beginstd::end (当然你应该使用),就不会有这个问题。 - James Kanze
@JamesKanze:确实如此;但是这位作者没有。当然,如果您有它们,那么您可能已经拥有了初始化器列表,甚至可以减少错误的范围。 - Mike Seymour
显示剩余3条评论
5个回答

10
如果你的编译器和库支持C++11,那么返回初始化列表即可:
std::vector<std::string> Object::getTypes(){
    return {"type1","type2", "type3"};
}

5
你找到的代码更加高效(因为types[]只分配了一次,而push_back可能会导致重新分配)。不过这种差异微不足道,除非你在一个(相对较大的)循环中调用getTypes,否则根本不需要考虑(即使在大循环中调用它,也可能不会有太大关系)。
因此,除非它产生了实际的性能问题,这只是一种风格选择。

3
基本上这是一种样式选择。我可能会做更像这样的事情:
std::vector<std::string> Object::getTypes(){
    static std::string types [] = {"type1","type2", "type3"};
    return std::vector<std::string> (types, 
                   types + (sizeof(types)/sizeof(std::string)) );
}

这个功能允许你在不必记得在下一行更新计数的情况下更改类型中的事物数量。


如果我像这样尝试,它会给我一个错误:"No matching constructor for initialization of 'std::vectorstd::string'"。最初我认为我应该按照你的说法去做,但它无法编译。 - Andres Bucci
@AndresBucci 你需要使用 std::vector<std::string> (types , types + sizeof(types)/sizeof(std::string)); - juanchopanza
1
@AndresBucci - 请查看编辑,juanchopanza的评论比我的编辑早了一点。基本上,向量没有(Type*,nelements)构造函数,它有一个(iterator,iterator)构造函数。 - Michael Kohne
我本想将您的答案选为被采纳的答案,但鉴于我选择的那个答案包含有解释性内容,我认为这对未来的读者更有帮助。 - Andres Bucci
使用 std::begin(types), std::end(types) 作为向 vector 构造函数传递的参数有什么问题吗?(如果您没有 C++11,则当然可以使用工具包中相应的函数。) - James Kanze

2
在第一个例子中,数组types被声明为静态(static)。这意味着它只存在于内存中一次。因此,有三种选项可供返回,它们都存在于静态内存中。然后,当你创建要返回的向量(vector)时,你可以通过将数组的起始和结束作为迭代器来一次性分配它的内存。
这样做的好处是,你不需要连续调用push_back,这意味着向量不需要重新分配其内部块的内存。
此外,在返回调用的过程中构造向量时,旧版本编译器会更容易进行返回值优化

RVO适用于所有代码。即使是“旧”的编译器(其中许多实际上比一些更近期的编译器做得更好)。 - James Kanze

0

我喜欢使用迭代器的这种初始化方式(以及C++11的统一初始化和初始化列表)的一个原因是它有助于将数据与代码分离。

反复使用push_back会让人感到不舒服,因为我迫切需要重构它。此外,当您真正只需要使用数据初始化容器时,您希望看到数据列表,而不是生成数据的代码。您在原始版本中找到的方法更符合这个原则。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接