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

+1

Почему все ваши аргументы отмечены 'const' несмотря на то, передается по значению? Вы пытаетесь обеспечить, чтобы код был настолько неэффективным, насколько это возможно? :) – Praetorian

ответ

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}; 
+0

Спасибо! Почему вы использовали 'std :: move ', в отличие от назначения, как было? – bcf

+0

@David То, что у вас было, было присвоением, которое бы скопировало исходный векторный элемент по элементу в пункт назначения. [Перемещение] (http://stackoverflow.com/questions/3106110/what-is-move-semantics) намного эффективнее. – Praetorian

+0

Не знаю почему мне нравится термин «массовое неопределенное поведение» так много xD. Извините за испорченный комментарий. – luk32

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>&.

+0

Спасибо, я просто изменил функцию 'getBase()', как и у вас, и она сработала, но она по-прежнему работала без использования 'cbegin()'. Я что-то пропустил? – bcf

+0

@David ['vector :: begin'] (http: //en.cppreference.com/w/cpp/container/vector/begin) имеет перегрузку, которая возвращает константный итератор. Поэтому нет необходимости использовать 'vector :: cbegin' (хотя это тоже сработает). Код не сработает после изменения 'getBase()', если вы попытаетесь изменить векторный элемент с помощью итератора; например, '* it = 0;' внутри цикла не будет компилироваться. – Praetorian

0

Вы с использованием UB

С auto it = num.getBase().begin() a d, 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;}

Смежные вопросы