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

将std::vector作为输入/输出引用参数传递时丢失了一些数据

  •  2
  • guenis  · 技术社区  · 12 年前

    我在函数之间传输包含在向量中的一些数据时遇到了问题。情况如下:

    void generateObjects(std::vector<MyClass> &objects)
    {
        objects.clear();
        //Fill objects vector
        std::vector<MyClass> p;
    
        //This 4-line pattern is repeated a number of times to generate all objects and store them in variable 'objects'
        p.clear();
        generateSomeOfTheObjects(p); //p is again passed by ref. in/out parameter
        for(uint j = 0; j < p.size(); p++){
            objects.push_back(p[j]);
        }
    
        //Print some members of the objects - works fine
        for(uint i = 0; i < objects.size(); i++){
            printf("%f ",objects[i].mymember->myElm);
        }
    }
    
    int main()
    {
       std::vector<MyClass> objects;
       generateObjects(objects);
       //Print size of vector - size is correct it is the same as it is in generateObjects func
       printf("%lu\n",objects.size());
       //Again print members of the objects - some members are retained after the function call, some are lost. 
       //The one below doesn't work, mymember is a pointer to another object and its member myElm seems not initialized.
       for(uint i = 0; i < objects.size(); i++){
           printf("%f ",objects[i].mymember->myElm);
       }
       //Here I need to pass the objects to another read-only function
       ...
    }
    

    我在互联网上搜索过类似的案例,实际上发现了很多,但我无法对我的代码应用相同的修复程序。我正试图访问MyClass实例的成员所指向的对象的成员(objects[I].mymember->myElm)这里可能缺少什么?

    2 回复  |  直到 12 年前
        1
  •  2
  •   rodrigo    12 年前

    可能错误在于 MyClass 。我想说,这个类包含一些用局部变量的地址初始化的指针,所以当你从一些函数返回时,指针指向一个被破坏的对象。

    那会的 未定义的行为 但是 也许 偶然工作。当您从第一个函数返回时,堆栈内存最终会被覆盖,数据也会丢失。

    更新: 感谢@chris在下面评论中的见解,最有可能的原因是 类名 没有复制构造函数,但它有一个指针成员。

    类似于以下内容:

    class MyClass
    {
    public:
        Member *mymember;
    
        MyClass()
        {
            mymember = new Member;
        }
        ~MyClass()
        {
            delete mymember;
        }
    };
    

    现在,如果使用编译器生成的默认复制构造函数(或复制运算符),会发生什么?

    void foo()
    {
        MyClass a;
        {
            MyClass b(a);
        }
        //a.mymember is no longer valid
    }
    

    二者都 a b 共享同一指针 mymember ,所以当其中一个被摧毁时 我的成员 被删除,而另一个保持一个悬空指针。

    这就是为什么我们有 三规则 .其中规定:

    每当定义非默认析构函数时,您很可能还需要一个非默认复制构造函数和一个非缺省复制运算符。

    现在您必须决定是否要共享 我的成员 或者如果你想复制它。第一个最好用智能指针完成( shared_ptr )第二个是深度复制。

    例如,深度复制:

    class MyClass
    {
    public:
        Member *mymember;
    
        MyClass()
        {
            mymember = new Member;
        }
        MyClass(const MyClass &c)
        {
            mymember = new Member(c.mymember);
        }
        MyClass &operator=(const MyClass &c)
        {
            if (this != &c) //be aware of self-copy
            {
                delete mymember;
                mymember = new Member(c.mymember);
            }
            return *this;
        }
        ~MyClass()
        {
            delete mymember;
        }
    };
    

    使用共享指针:

    class MyClass
    {
    public:
        std::shared_ptr<Member> mymember; //or boost::shared_ptr if old compiler
    
        MyClass()
            :mymember(new Member)
        {
        }
        //no custom-made destructor -> no rule of 3
    };
    
        2
  •  0
  •   Aesthete    12 年前

    也许与你的问题无关,但这:

    void generateObjects(std::vector<MyClass> &objects)
    {
      objects.clear();
      std::vector<MyClass> p;
      p.clear();
    
      generateSomeOfTheObjects(p);
      for(uint j = 0; j < p.size(); p++){
          objects.push_back(p[j]);
      }
    
      for(uint i = 0; i < objects.size(); i++){
          printf("%f ",objects[i].mymember->myElm);
      }
    }
    

    与此相同:

    void generateObjects(std::vector<MyClass> &objects)
    {
      objects.clear();
      generateSomeOfTheObjects(objects);
      std::reverse(objects.begin(), objects.end()); 
      for(uint i = 0; i < objects.size(); i++) {
          printf("%f ",objects[i].mymember->myElm);
      }
    }
    

    正如@rodrigo所提到的,您的复制问题是您没有使用复制构造函数进行深度复制。