为什么迭代器没有在这段代码中初始化

时间:2014-05-27 04:29:44

标签: c++ vector iterator copy-constructor

在下面的代码中,A类有一个整数向量。 B类继承自A并且具有向量的迭代器。 B的构造函数初始化B的迭代器。但是当我们打印时,我们得到了垃圾。为什么主打印垃圾?我错过了什么吗?

#include <iostream>
#include <vector>

class A { 
public:
    A() {}
    A(const A& copyFrom) : intList(copyFrom.intList) {} //copy constructor
    virtual ~A() {}

    void populateList() 
    {
        for(int i=10; i<100; i++) 
        {
            intList.push_back(i);
        }
    }

protected:
    std::vector<int> intList;
};

class B : public A {
public:
    B() : A() {}
    B(const A& a) : A(a), intIt(intList.begin()) 
    {
        std::cout << "constructor with base class param called" << std::endl;
    }

    B(const B& b) : A(b), intIt(intList.begin()) //copy constructor
    { 
        std::cout << "copy constructor called" << std::endl;
    } 


    std::vector<int>::iterator intIt;
};

int main() {
A a;
a.populateList();
B b ;
b = B(a);
std::cout << *b.intIt << std::endl; //prints garbage. why?
}

谢谢!

3 个答案:

答案 0 :(得分:4)

B b ;
b = B(a); //the copy constructor of B is not called here. Why? 

该行中的操作是赋值,而不是复制构造函数。复制构造函数将是:

B b = someotherB;

请注意,从语义上讲,这也是复制构造:

B b = B(a);

但编译器通常会通过创建B(a)代替b来避免复制构造。

答案 1 :(得分:3)

与声明分离的表达式b = B(a)赋值,而不是复制构造。这是关于C ++的一个令人困惑的事情:

Foo x(y);      // copy-construction
Foo x = y;     // also copy-construction, equivalent to the above

Foo x; x = y;  // default-construction followed by assignment, different
               // (and possibly less efficient) than the above

未能定义赋值运算符将创建执行成员分配的默认运算符。对于intList成员,这是可以的,但是分配的intIt将继续指向旧列表,在您的情况下,旧列表属于B创建的B(a)的临时引用表达。只要该引用被销毁(位于最外层表达式的末尾),它的intList就会被销毁,迭代器也会变得无效。

换句话说,B b; b = B(a)相当于:

B b;         // constructor
{
  B tmp(a);  // copy constructor
  b = tmp;   // member-wise assignment
  // at this point, b.intList is a copy of tmp.intList, but b.intIt
  // points to the beginning of tmp.intList, not to the beginning of
  // b.intList
}
// tmp is destroyed and b.intIt now points to the beginning of a deleted list

您有责任定义一个赋值运算符,该运算符维护intIt的不变量,引用intList中的元素;例如:

B& operator=(const B& rhs)
{
    intList = rhs.intList;
    intIt = intList.begin() + (rhs.intIt - rhs.intList.begin());
    std::cout << "assignment called" << std::endl;
    return *this;
} 

另请注意,当您更改向量的大小时,向量中的所有迭代器都会失效,即使您只附加push_back()。因此populateList()和其他可以更改向量大小的方法应该在B中重写,以便重新计算intIt。由于这些原因,最好避免使用迭代器并将位置简单地存储到向量中,并且有一个返回intList.begin() + pos的函数,从而按需创建一个保证有效的迭代器。然后你不需要既不定义复制构造函数也不定义赋值运算符,因为编译器提供的默认值可以正常工作。

答案 2 :(得分:0)

不会调用复制构造,因为您没有分配相同类的对象

b = B(a); //the copy constructor of B is not called here. Why? 

在上面的代码中,将调用构造函数,因为您传递了类型为A的对象

B(const A& a) : A(a), intIt(intList.begin()) 
{
    std::cout << "constructor with base class param called" << std::endl;
}

迭代器值将无法使用,因为您没有初始化它

std::cout << *(b.intIt) << std::endl; //prints garbage. why?

你需要将其初始化

 std::vector<X>::iterator iter;  //no particular value
 iter = some_vector.begin();  //iter is now usable