【问题标题】:Initialise const std::vector Class Members Efficiently有效地初始化 const std::vector 类成员
【发布时间】:2019-01-16 20:03:24
【问题描述】:

假设我正在尝试创建“不可变”类对象(即成员变量是用const 定义的)。因此,在调用构造函数时,我当前调用单独的 init 函数来初始化类成员。但结果似乎有很多新的矢量创建和矢量复制正在进行。

如果成员不是const,我可以在构造函数的{ } 部分执行初始化并直接写入values(我认为这样会更有效)。但这是不可能的。

是否有更好/更清洁/更有效的方法来设计不可变类的构造?

#include <vector>

class Data
{
public:
    const std::vector<int> values;

    Data(unsigned int size, int other) : values(init(size, other)) { }
    Data(const std::vector<int>& other) : values(init(other)) { }

private:
    std::vector<int> init(unsigned int size, int other) {
        std::vector<int> myVector(size);

        for (unsigned int i = 0; i < size; ++i)
            myVector[i] = other * i;

        return myVector;
    }

    std::vector<int> init(const std::vector<int>& other) {
        std::vector<int> myVector(other);

        for (unsigned int i = 0; i < other.size(); ++i)
            myVector[i] *= myVector[i] - 1;

        return myVector;
    }
};

int main() {
    Data myData1(5, 3);         // gives {0, 3, 6, 9, 12}
    Data myData2({2, 5, 9});    // gives {2, 20, 72}

    return 0;
}

【问题讨论】:

  • 为什么你认为有很多抄袭?你有没有对任何东西进行基准测试?
  • 我希望来自init()的本地向量被移动到成员中,应该没有复制。

标签: c++ performance c++11 vector constructor


【解决方案1】:

您当前的设计非常好。初始化发生在构造函数的成员初始化列表中,因此它会在最坏的情况下触发移动(无论如何这对于向量来说非常便宜),而在最好的情况下会触发 NRVO

NRVO命名返回值优化。当函数返回具有自动存储持续时间的命名变量时,允许编译器省略复制/移动。但是请注意,即使在省略的情况下,复制/移动构造函数仍然需要可用。这是一个总结该概念的虚拟示例:

SomeType foo() { // Return by value, no ref
    SomeType some_var = ...; // some_var is a named variable
                             // with automatic storage duration
    do_stuff_with(var);
    return some_var; // NRVO can happen
}

(您的 init 函数遵循该模式。)

在 C++17 中,根据 init 函数的形状,您甚至可以从保证复制省略中受益。您可以在另一个SO answer 中找到更多相关信息。

注意:既然你标记了你的问题 我假设移动语义是可用的。

【讨论】:

    【解决方案2】:

    你说

    似乎有很多新的矢量创建和矢量复制正在进行。

    但我不确定。相反,我希望在这里有一个完整的创作和一个举动: init 构建并返回一个临时向量(可以创建完整向量,直接使用最终大小),用于初始化 const 成员(好的,应该在这里进行移动)。我们应该在这里控制生成的程序集,但是一个像样的编译器应该构建一次数据块,并将其移动到数据成员中。

    因此,除非您可以通过分析(或通过查看编译器生成的程序集)证明这里确实需要优化,否则我很乐意继续使用此代码,因为它清楚地声明了成员常量。

    【讨论】:

      【解决方案3】:

      这里的解决方案是从成员向量中删除const,以便您可以就地执行初始化,而不是通过复制。

      如果您希望 values 对类的用户可读但不可写,您可以公开对它的 const 引用:

      class Data {
          std::vector<int> values_;
      
          // constructors...
      
      public:
          std::vector<int> const& values() const { return values_; }
      };
      

      【讨论】:

      • 对我来说,公开对内部数据的引用是糟糕的设计。如果类的实例是数据的所有者,那么将操作也放在该类中是有意义的。例外地,该类仅设计为容器,但此用例必须有充分的基础。
      猜你喜欢
      • 2018-10-17
      • 2012-08-07
      • 1970-01-01
      • 2020-10-07
      • 2012-12-18
      • 1970-01-01
      • 1970-01-01
      • 2022-07-12
      • 2016-05-03
      相关资源
      最近更新 更多