代码之家  ›  专栏  ›  技术社区  ›  Pietro

如何调用std::vector中包含的对象的构造函数?

  •  11
  • Pietro  · 技术社区  · 14 年前

    当我创建对象的std::vector时,并不总是调用这些对象的构造函数。

    #include <iostream>
    #include <vector>
    using namespace std;
    
    struct C {
        int id;
        static int n;
        C() { id = n++; }   // not called
    //  C() { id = 3; }     // ok, called
    };
    
    int C::n = 0;
    
    
    int main()
    {
        vector<C> vc;
    
        vc.resize(10);
    
        cout << "C::n = " << C::n << endl;
    
        for(int i = 0; i < vc.size(); ++i)
            cout << i << ": " << vc[i].id << endl;  
    }
    

    这是我得到的结果:

    C::n = 1
    0: 0
    1: 0
    2: 0
    ...
    

    这就是我想要的:

    C::n = 10
    0: 0
    1: 1
    2: 2
    ...
    


    原因可能是向量的元素没有按顺序初始化,从第一个到最后一个,所以我不能获得确定性行为吗?

    谢谢你的朋友!

    4 回复  |  直到 14 年前
        1
  •  5
  •   Community Egal    7 年前

    原因是 vector::resize 通过调用自动提供的复制构造函数(而不是在示例中定义的构造函数)插入副本。

    struct C {
    //....
    C(const C& other) {
        id = n++;
        // copy other data members
    }
    //....
    };
    

    但是由于vector::resize的工作方式(它有第二个可选参数用作它创建的副本的“原型”,在您的示例中有一个默认值) C() ),这将在示例中创建11个对象(“原型”和10个副本)。

    :

    这个解决方案有几个缺点值得注意,还有一些选项和变体可能会产生更易于维护和更合理的代码。

    • like this ),这也可以说是更好的面向对象设计,但当然你也要记住 the many issues that can crop up with multiple inheritance

    • 它会让其他人更难理解,因为一份拷贝已经不再是大多数人所期望的了。类似地,处理类(包括标准容器)的其他代码可能会出现错误行为。解决这个问题的一个方法是定义一个 operator== 你的类的方法(和它) may be argued 在重写copy构造函数(即使不使用该方法)时,这是一个好主意,以保持它在概念上的“健全”,并作为一种内部文档。如果你的类得到了大量的使用,你很可能最终也会提供一个 operator= 这样您就可以保持自动生成的实例id与应该在此操作符下进行的类成员分配之间的分离。以此类推;)

    • 如果您对程序有足够的控制权来使用动态创建的实例(通过new)并使用指向容器中那些实例的指针,那么就可以消除“副本的不同id值”这一问题的歧义。这确实意味着您需要在某种程度上“手动”初始化元素,但编写一个函数来返回一个充满指向新的初始化实例的指针的向量并不需要很多工作。如果您在使用标准容器时始终处理指针,那么您就不必担心标准容器会“隐藏”地创建任何实例。

    如果您知道所有这些问题,并且相信您能够处理这些后果(这当然高度依赖于您的特定上下文),那么重写复制构造函数是一个可行的选择。毕竟,语言的特点是有原因的。显然,这并不像看上去那么简单,你应该小心。

        2
  •  21
  •   James McNellis    14 年前

    并不总是调用这些对象的构造函数。

    resize() 实际上是这样声明的:

    void resize(size_type sz, T c = T());
    

    复制 每个新插入的向量元素。如果省略第二个参数,它将默认构造一个类型为的对象 T

    在你的代码里,一个临时的 C 构造并调用默认构造函数; id

    [感兴趣的注意事项:在C++ 03中,第二个参数 调整大小() ( c )价值观;在C++ 0x中,它由const LValk引用(参见 LWG Defect 679

    在这个例子中,我是否被迫调整向量的大小,然后“手动”初始化它的元素?

    您可以(也可能应该)将元素单独插入到向量中,例如。,

    std::vector<C> vc;
    for (unsigned i(0); i < 10; ++i)
        vc.push_back(C());
    
        3
  •  3
  •   user180326 user180326    14 年前

        4
  •  0
  •   Pietro    14 年前


    这就是我的意思:

    #include <iostream>
    #include <vector>
    #include <deque>
    using namespace std;
    
    struct C {
        int id;
        static int n;
        int data;
    
        C() {               // not called from vector
            id = n++;
            data = 123;
        }
    
        C(const C& other) {
            id = n++;
            data = other.data;
        }
    
        bool operator== (const C& other) const {
            if(data == other.data)      // ignore id
                return true;
            return false;
        }
    };
    
    int C::n = 0;
    
    
    int main()
    {
        vector<C> vc;
        deque<C> dc;
    
        vc.resize(10);
    
        dc.resize(8);
    
        cout << "C::n = " << C::n << endl;
    
        for(int i = 0; i < vc.size(); ++i)
            cout << "[vector] " << i << ": " << vc[i].id << ";  data = " << vc[i].data << endl;
    
        for(int i = 0; i < dc.size(); ++i)
            cout << "[deque] " << i << ": " << dc[i].id << ";  data = " << dc[i].data << endl;
    }
    

    C::n = 20
    [vector] 0: 1;  data = 123
    [vector] 1: 2;  data = 123
    [vector] 2: 3;  data = 123
    [vector] 3: 4;  data = 123
    [vector] 4: 5;  data = 123
    [vector] 5: 6;  data = 123
    [vector] 6: 7;  data = 123
    [vector] 7: 8;  data = 123
    [vector] 8: 9;  data = 123
    [vector] 9: 10;  data = 123
    [deque] 0: 12;  data = 123
    [deque] 1: 13;  data = 123
    [deque] 2: 14;  data = 123
    [deque] 3: 15;  data = 123
    [deque] 4: 16;  data = 123
    [deque] 5: 17;  data = 123
    [deque] 6: 18;  data = 123
    [deque] 7: 19;  data = 123