【问题标题】:Some data lost while passing std::vector as in/out reference parameter将 std::vector 作为输入/输出参考参数传递时丢失了一些数据
【发布时间】:2012-07-23 23:06:28
【问题描述】:

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

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) 指向的对象的成员,我在这里可能遗漏了什么?

【问题讨论】:

  • 您需要显示填充向量的代码。你这里的东西看起来不错。
  • MyClass 中有哪些数据成员?
  • 你为什么要清除objects,创建一个新的vector p,填充然后复制到objects
  • @guenis,供您参考,Rule of Three。这可能是问题所在,也可能不是,但如果是这样,那将有所帮助。如果您的类在构造函数中为 mymember 分配内存,在析构函数中释放它,并且没有(正确的)复制(/移动)构造函数/赋值运算符,则很可能是问题所在。
  • 实现三规则正确性的简单方法是使用智能指针来避免它。 Proof your code works if MyClass is properly implemented

标签: c++ parameter-passing stdvector


【解决方案1】:

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

这将未定义的行为,但它可能偶然起作用。当你从第一个函数返回时,栈内存最终被覆盖,你的数据丢失了。

更新:感谢@chris 在下面的 cmets 中的洞察力,最可能的原因是您的 MyClass 没有复制构造函数,但它确实有一个指针成员。

类似这样的:

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
}

ab 共享同一个指针mymember,因此当其中一个被销毁时,mymember 被删除,另一个持有一个悬空指针。

这就是为什么我们有三规则。它指出:

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

现在您必须决定是要共享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
};

【讨论】:

  • 我在评论中考虑了三规则,但没有类定义我们不会知道。
  • @chris:你是对的。对象从p复制objects,因此默认的复制构造函数可能会导致此问题。这实际上更有可能。
  • 真的取决于它是保存数据还是创建数据。
  • 我对 C++ 不是很有经验,但是 chris 我想你找到了问题所在,类缺少复制构造函数
  • @rodrigo,随意编辑三规则并涵盖两个基础。根据问题的代码,这就是您可以获得的最佳答案。
【解决方案2】:

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

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 所提到的,您的复制问题是您没有使用复制构造函数进行深度复制。

【讨论】:

  • 正如我所说,这不是我的代码,所以它太乱了,我只想尽快修复它。不过谢谢你的帮助
猜你喜欢
  • 2014-03-05
  • 2020-05-06
  • 2015-12-09
  • 2019-10-17
  • 1970-01-01
  • 2016-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多