Итерация по элементу базового класса

0

У меня есть что-то вроде следующего:

#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 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) изменяется каждый раз, но остальные числа одинаковы. Заранее спасибо!

  • 1
    Почему все ваши аргументы помечены как const несмотря на то, что они передаются по значению? Вы пытаетесь сделать код максимально неэффективным? :)
Теги:
iterator
vector
inheritance

3 ответа

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)) {}
};

И с С++ 11 вы можете инициализировать вектор как

std::vector<int> v{6,3,7,6,2};
  • 0
    Спасибо! Почему вы использовали `std :: move ', а не как присвоение?
  • 0
    @David То, что у вас было, было назначением, которое копировало бы исходный векторный элемент за элементом в место назначения. Перемещение намного эффективнее, чем это.
Показать ещё 5 комментариев
0

Вы вызываете 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<T> const& getBase() const {return base;}

0
  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, insterad копии. Вам также нужно будет использовать std::vector::cbegin(), потому что begin() позволит вам изменить исходный вектор, что противоречит типу const std::vector<T>& return.

  • 0
    Спасибо, я только что изменил getBase() , и она работала, но она все еще работала без использования cbegin() . Я что-то пропустил?
  • 0
    @David vector::begin имеет перегрузку, которая возвращает константный итератор. Поэтому нет необходимости использовать vector::cbegin (хотя это тоже будет работать). После изменения getBase() код потерпит неудачу, если вы попытаетесь изменить элемент вектора с помощью итератора; например, *it = 0; внутри цикла не скомпилируется.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню