【问题标题】:Iterating through base class member遍历基类成员
【发布时间】:2014-01-04 07:22:59
【问题描述】:

我有类似以下的东西:

#include <vector>
#include <iostream>

template<typename T>
class Vector {
private:
  std::vector<T> base;

public:
  Vector(const std::vector<T> vec) {base = vec;}

  T& operator[](const int& index) {return base[index];}
  std::vector<T> getBase() const {return base;}
};


class BigNum : public Vector<int>
{
public:
  BigNum(const std::vector<int> init) : Vector(init) {}
};


int main()
{
  int arr[] = {6,3,7,6,2};
  std::vector<int> v(arr, arr + sizeof(arr) / sizeof(arr[0]));

  BigNum num(v);

  for(auto it = num.getBase().begin(); it != num.getBase().end(); ++it)
    {
      std::cout << *it << " ";  // What's going on here??
    }

  std::cout << "\n";

  for(int i = 0; i < 5; ++i)
    {
      std::cout << num.getBase()[i] << " ";
    }

  std::cout << "\n";
}

这两个循环的输出是:

30134336 0 7 6 2 
6 3 7 6 2 

这里发生了什么?第一个循环中的第一个数字(30134336)每次都会改变,但其余的数字都是一样的。提前致谢!

【问题讨论】:

  • 为什么所有参数都标记为const,尽管是按值传递的?您是否试图确保代码尽可能低效? :)

标签: c++ inheritance vector iterator


【解决方案1】:
 std::vector<T> getBase() const {return base;}

该函数返回存储的vector 的副本,因此您正在迭代 2 个(或每次迭代不同的vector)完全不同的向量,这些向量在创建后很快就被销毁了。大量未定义的行为。把函数改成

std::vector<T> const& getBase() const {return base;}

我会将您的课程改写为

template<typename T>
class Vector {
private:
  std::vector<T> base;

public:
  Vector(std::vector<T> vec)
  : base(std::move(vec))
  {}

  T& operator[](int index) {return base[index];}
  T const& operator[](int index) const {return base[index];}
  std::vector<T> const& getBase() const {return base;}
};


class BigNum : public Vector<int>
{
public:
  BigNum(std::vector<int> init) : Vector(std::move(init)) {}
};

而使用 C++11,您可以将向量初始化为

std::vector<int> v{6,3,7,6,2};

【讨论】:

  • 谢谢!你为什么使用`std::move',而不是像以前那样的赋值?
  • @David 你所做的是分配,它将源向量元素一个元素地复制到目标。 Moving 比这更有效。
  • 不知道为什么我这么喜欢“大规模未定义行为”这个词 xD。抱歉,评论被宠坏了。
  • @Praetorian 另外,第二个const operator[] 需要什么?
  • @David 您发布的示例中并不真正需要它。但是假设您向BigNum 添加了一个const 成员函数,您需要在其中访问继承的operator[],那么您将需要const 重载。
【解决方案2】:
  1. num.getBase() 创建base 成员Vector 的副本。
  2. num.getBase().begin() 为该副本创建一个迭代器。
  3. 分配auto it = num.getBase().begin() 后,由num.getBase() 创建的副本被销毁。这会使迭代器无效。
  4. 使用无效的迭代器是未定义的行为。任何事情都有可能发生。

您可以通过将Vector::getBase() 定义为

来解决此问题
const std::vector<T>& getBase() const { return base; }

这样,num.getBase() 将返回对原始 Vector::base 的引用,即副本的中间部分。您还必须使用std::vector::cbegin(),因为begin() 将允许您修改原始向量,这与const std::vector&lt;T&gt;&amp; 返回类型相矛盾。

【讨论】:

  • 谢谢,我刚刚更改了 getBase() 函数,因为它可以正常工作,但它仍然可以正常工作,而无需使用 cbegin()。我错过了什么吗?
  • @David vector::begin 有一个返回常量迭代器的重载。所以没有必要使用vector::cbegin(尽管这也可以)。如果您尝试使用迭代器修改向量元素,则更改 getBase() 后代码将失败;例如,循环内的*it = 0; 将无法编译。
【解决方案3】:

你正在调用一个 UB

因为 auto it = num.getBase().begin() 和 ,num.getBase().end() 是两个不同的向量迭代器。

你可以使用:

  auto v1= num.getBase();
  for(auto it = v1.begin(); it != v1.end(); ++it)
  {
      std::cout << *it << " "; 
  }

或者把getbase()改成

std::vector&lt;T&gt; const&amp; getBase() const {return base;}

【讨论】:

    猜你喜欢
    • 2013-10-04
    • 2011-06-27
    • 2012-07-03
    • 2020-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-18
    • 1970-01-01
    相关资源
    最近更新 更多