用向量替换class new []变量 - 移动,复制运算符

时间:2015-02-25 16:07:27

标签: c++ c++11 vector move

我为我正在做的一些工作制作了一个稀疏矩阵类。对于稀疏结构,我使用指针,例如int* rowInd = new int[numNonZero]。对于我写的副本和移动赋值运算符,所有工作正常。

在网上阅读关于移动和复制语义,我切然发现了一种压倒性的观点,即在现代C ++中我可能不应该使用原始指针。如果是这种情况,那么我想修改我的代码以使用向量进行良好的编码实践。

  1. 我主要阅读原始指针上的向量。有没有理由不改变为载体?
  2. 如果我将数据更改为存储在向量而不是new []数组中,是否仍需要手动编写类的复制/移动赋值和构造函数运算符? vector和new [] move / copy操作符之间是否有任何重要的区别?
  3. 假设我有一个名为Levels的类,它包含几个稀疏矩阵变量。我想要一个函数来创建Levels的向量,并返回它:
  4. vector<Levels> GetGridLevels(int &n, ... ) { 
        vector<Levels> grids(n);       
        \\ ... Define matrix variables for each Level object in grids ...
        return grids;
    }
    

    移动语义会阻止它成为昂贵的副本吗?我会这么认为,但它是包含成员矢量变量的对象的矢量,这似乎很多......

3 个答案:

答案 0 :(得分:2)

是的,请使用std::vector<T>代替原始T *

同样是的,编译器会为您生成复制和移动赋值运算符,这些运算符很可能具有最佳性能,因此不要自己编写。如果你想要明确,你可以说你想要生成的默认值:

struct S
{
  std::vector<int> numbers {};

  // I want a default copy constructor
  S(const S&) = default;

  // I want a default move constructor
  S(S &&) noexcept = default;

  // I want a default copy-assignment operator
  S& operator=(const S&) = default;

  // I want a default move-assignment operator
  S& operator=(S&&) noexcept = default;
};

关于你的上一个问题,如果我理解正确,你的意思是return移动感知类型的值是否有效。是的,它会的。要充分利用编译器的优化,请遵循以下规则:

  • 按值返回(不是const值,这会阻止移动)。

  • 不要return std::move(x),只需return x(至少如果你的回复类型是decltype(x)),那么就不要禁止复制省略。

  • 如果您有多个return语句,return每个路径上都有相同的对象,以便于命名返回值优化(NRVO)。

    std::string
    good(const int a)
    {
      std::string answer {};
      if (a % 7 > 3)
        answer = "The argument modulo seven is greater than three.";
      else
        answer = "The argument modulo seven is less than or equal to three.";
      return answer;
    }
    
    std::string
    not_so_good(const int a)
    {
      std::string answer {"The argument modulo seven is less than or equal to three."};
      if (a % 7 > 3)
        return "The argument modulo seven is greater than three.";
      return answer;
    }
    
  • 对于那些编写移动构造函数和赋值运算符的类型,请确保声明它们noexcept或某些标准库容器(尤其是std::vector)将拒绝使用它们。

答案 1 :(得分:1)

  1. 与正确无关。请注意,构建大小为n的向量意味着它将初始化其所有元素,因此您可能更愿意构造一个空向量,然后reserve(n),然后push_back元素。< / LI>
  2. 不,隐式移动构造函数/赋值应该全部处理 - unless you suppress them
  3. 是的,如果您没有编写代码来阻止移动,那么您将自动从std::vector开始有效移动。
  4. 另外,请考虑使用现有的库,例如Eigen,这样您就可以免费获得一些相当优化的例程。

答案 2 :(得分:1)

  1. 没有。在99%的情况下,最简单地使用std :: vector将比原始指针更好,更安全,并且在需要手动管理内存的不太常见的情况下,这些类可以使用自定义分配器/解除分配器(用于例如,如果你想使用对齐的内存来使用对齐的SSE内在函数)。如果使用自定义分配器,代码可能比原始指针更复杂,但更易于维护,更不容易出现内存问题。

  2. 根据您的其他成员以及您的课程所做的事情,您可能需要实施移动/复制分配/ ctors。但这会更简单。您可能必须自己实现它们,但对于您的向量,您只需要调用相应的运算符/ ctors。代码将简单易读,您不会有段错误/内存泄漏的风险

  3. 是的,但移动语义甚至不是必需的。返回值优化将负责优化副本(实际上不会有副本)。但这是编译器特定的,并且不受标准保证。