2

関数間でベクトルに含まれるデータを転送するのに問題があります。状況は次のとおりです。

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)が指すオブジェクトのメンバーに到達しようとしています。ここで何が欠落している可能性がありますか?

4

2 に答える 2

2

おそらくエラーは の実装にありMyClassます。このクラスには、ローカル変数のアドレスで初期化されるポインターが含まれていると思います。そのため、一部の関数から戻ると、ポインターは破棄されたオブジェクトを指します。

これは未定義の動作ですが、偶然に機能する可能性があります。最初の関数から戻ると、スタック メモリは最終的に上書きされ、データは失われます。

更新:以下のコメントの @chris による洞察のおかげで、最も可能性の高い理由は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
}

aとはb同じポインタを共有しているmymemberため、一方が破棄mymemberされると が削除され、もう一方はダングリング ポインタを保持します。

そのため、3 のルールがあります。それは述べています:

デフォルト以外のデストラクタを定義するときはいつでも、デフォルト以外のコピーコンストラクタとデフォルト以外のコピー演算子も必要になるでしょう。

次に、 の所有権を共有するmymemberか、コピーするかを決定する必要があります。1 つ目はスマート ポインター ( shared_ptr) を使用するのが最適であり、2 つ目はディープ コピーを使用するのが最適です。

たとえば、ディープ コピー:

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
};
于 2012-07-23T23:26:59.950 に答える
0

おそらくあなたの質問とは関係ありませんが、これは:

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が述べたように、コピーの問題は、コピーコンストラクターでディープコピーを行っていないことです。

于 2012-07-23T23:32:11.333 に答える