为什么这个嵌套类中的指针丢失了?

时间:2017-12-07 16:24:10

标签: c++ class pointers nested operator-overloading

简短问题:

B类有一个ptr到C类,它的D类有一个ptr到B类

通过复制将类B分配给A类中的数组,期望看到p​​tr指向数组中的新实例而不是原始实例但是失败。

我已经能够做一些解决方法,但我想知道为什么我原来的方法失败了。

更详细的解释如下,并且还会发布重现问题的代码

任何能够解释正在发生的事情的人都会受到赞赏。

共有6个课程:

类CastInfo //包含一个Character *

class Skill //抽象类,包含CastInfo

class Movvement:public Skill

班级Move1:公共运动

class Character //包含一个Movement *,在实践中将是Move1 *

class Squad //包含一个Character

数组

具有以下关系:

  1. CastInfo中的字符*应指向拥有该技能的角色 是CastInfo的所有者

  2. 将技能分配给角色时,CastInfo中的角色*指向该角色

  3. 应该复制Squad数组中的Character,因此会有2个实例,而CastInfo中的Character *也应该指向Squad数组中的Character而不是原始实例

  4. 期待的结果是:

    1. move1!= ch1.move1!= squad.ch [0] .move1(已经满意)

    2. ch1.move1-> cast_info.caster ==& ch1!= squad.ch [0] .move1-> caster_info.caster(这是问题)

    3. 输出有2种情况(尝试过):

      在Squad的构造函数中: 如果使用

      characters_[i] = characters[i];
      

      正确复制了该角色,但该技能位于同一地址

      move1: 00000270E6093500
      ch1: 000000BC6DCFF378
      ch1.move1: 00000270E6093E60
      ch1.move1->cast_info.caster: 000000BC6DCFF378
      squad.ch[0]: 000000BC6DCFF3E0
      squad.ch[0].move1: 00000270E6093E60
      squad.ch[0].move1->cast_info.caster: 000000BC6DCFF378
      

      如果使用

      characters_[i] = Character(characters[i]);
      

      该角色被正确复制,但缺少技能(指向一些奇怪的位置)

      move1: 00000230FDCEF080
      ch1: 00000058A11DF548
      ch1.move1: 00000230FDCEF260
      ch1.move1->cast_info.caster: 00000058A11DF548
      squad.ch[0]: 00000058A11DF5B0
      squad.ch[0].move1: 00000230FDCEF0E0
      squad.ch[0].move1->cast_info.caster: 00000058A11DF378
      

      在第一种情况下,我想这可能是因为我没有重载operator =,所以只复制地址。我试图超载它,但它引起了更多的问题。 (例如使用Builder.Build()时)

      在第二种情况下,我希望它首先调用复制构造函数,它触发调用SetCaster()的SetMove1()。如图所示克隆了move1,但我无法理解为什么施法者没有正确更新。 (虽然调用operator =在construnction之后,地址应该保持不变。)

      以下代码应重现此问题:

      motion.h

      #pragma once
      class Character;
      
      struct CastInfo
      {
          Character* caster;
          int coeff;
      };
      
      class Skill
      {
      public:
          CastInfo cast_info;
          Skill() {};
          ~Skill() {};
          virtual void DoSomething() = 0;
      };
      
      class Movement : public Skill
      {
      public:
          Movement();
          ~Movement();
          virtual void DoSomething() { ; }
          virtual Movement* Clone() const { return new Movement(*this); }
      };
      
      class Move1 : public Movement
      {
      public:
          Move1() { cast_info.coeff = 123; }
          void DoSomething() { ; }
          virtual Move1* Clone() const { return new Move1(*this); }
      };
      
      class Move2 : public Movement
      {
      public:
          void DoSomething() { ; }
      };
      

      motion.cpp:

      #include "motion.h"
      Movement::Movement() { }
      Movement::~Movement() { }
      

      test.h:

      #pragma once
      #include <string>
      #include <vector>
      #include "motion.h"
      #define SQUAD_SIZE 6
      
      extern Movement* null_movement;
      class Character
      {
      public:
          class Builder;
          Character();
          ~Character();
          Character(const Character& character);
          Character& SetMove1(Movement* skill);
      public:
          int id_;
          Movement* move1_ = null_movement;
          Movement* move2_ = null_movement;
          Character(int id) : id_(id) { ; }
          void SetCaster();
      };
      
      class Character::Builder : public Character
      {
      public:
          Builder& SetId(int i) { id_ = i; return *this; }
          Character Build() { return Character(id_); }
      };
      
      class Squad
      {
      public: 
          class Builder;
          Squad() { }
          Squad(const Squad& squad);
          ~Squad() { }
      public:
          Character characters_[SQUAD_SIZE];
          Squad(Character* characters);
      };
      
      class Squad::Builder :public Squad
      {
      public:
          Builder& SetCharacter(const Character& character, const int position) { characters_[position] = character; return *this; }
          Squad Build() { return Squad(characters_); }
      };
      

      TEST.CPP

      #include <iostream>
      #include "test.h"
      
      Movement* null_movement = new Move2();
      Character::Character() : id_(0) { }
      Character::~Character() {}
      Character::Character(const Character& character) {
          id_ = character.id_;
          SetMove1(character.move1_);
      }
      
      Character& Character::SetMove1(Movement* move1) {
          if (!move1) return *this;
          move1_ = move1->Clone(); 
          SetCaster();
          return *this;
      }
      
      void Character::SetCaster() { 
          if (move1_ != NULL) move1_->cast_info.caster = this;
      }
      
      Squad::Squad(const Squad& squad) {
          *this = squad;
      }
      
      Squad::Squad(Character* characters) {
          for (int i = 0; i < SQUAD_SIZE; i++) {
              //characters_[i] = characters[i];  //character copied, skill same address
              characters_[i] = Character(characters[i]); //character copied, skill missing
          }
      }
      

      的main.cpp

      #include <iostream>
      #include "test.h"
      #include "motion.h"
      int main() {
      
          Move1* move1 = new Move1();
          std::cout << "move1: " << move1 << std::endl;
      
          Character ch1 = Character::Builder().SetId(1).Build();
          Character ch2 = Character::Builder().SetId(2).Build();
          ch1.SetMove1(move1);
      
          std::cout << "ch1: " << &ch1 << std::endl;
          std::cout << "ch1.move1: " << (ch1.move1_) << std::endl;
          std::cout << "ch1.move1->cast_info.caster: " << (ch1.move1_->cast_info.caster) << std::endl;
      
          Squad squad = Squad::Builder().SetCharacter(ch1, 0).SetCharacter(ch2, 1).Build();
      
          std::cout << "squad.ch[0]: " << &(squad.characters_[0]) << std::endl;
          std::cout << "squad.ch[0].move1: " << (squad.characters_[0].move1_) << std::endl;
          std::cout << "squad.ch[0].move1->cast_info.caster: " << (squad.characters_[0].move1_->cast_info.caster) << std::endl;
      
          system("PAUSE");
          return 0;
      }
      

      如前所述,我有一个解决方法来实现我的目标:

      创建另一个方法,在Squad中遍历数组,并调用每个Character的SetCaster()方法。

      void Squad::SetCaster() {
          for (int i = 0; i < SQUAD_SIZE; i++) {
              characters_[i].SetCaster();
          }
      }
      

      但我认为这很脏,因为每次在Builder :: Builder()之后,必须调用SetCaster(),这是不直观且容易出错的。

1 个答案:

答案 0 :(得分:0)

我想我发现了问题,如下图所示:

问题在于

Squad::Squad(Character* characters) {
    for (int i = 0; i < SQUAD_SIZE; i++) {
        //characters_[i] = characters[i];  //character copied, skill same address
        characters_[i] = Character(characters[i]); //character copied, skill missing
    }
}

如上所述,使用注释行只是复制值,这是不正确的。

什么

characters_[i] = Character(characters[i]); //character copied, skill missing

如下:

  1. 创建一个Character,通过调用Character的构造函数,该对象位于地址A.调用SetMove1(),调用SetCaster()。 cast_info中的指针正确指向A.

  2. 将对象分配给其地址位于地址B的characters_ [i],,因为在创建Squad时分配了字符_的地址。由于我没有重载Character :: operator =,指针仍然指向地址A

  3. 构造函数完成后,Squad返回。

  4. 这就是原因

    std::cout << "squad.ch[0].move1->cast_info.caster: " << (squad.characters_[0].move1_->cast_info.caster) << std::endl;
    

    显示第三个地址(地址A)既不是&#;(字符[0])(地址B)也不是&amp; ch1(原始字符的地址)

    解决方案是重载operator =或者在for循环之后立即在构造函数中放入我的“workaround”(Squad :: SetCaster())。

    如果有任何错误,或者有更好的解决方案,请纠正我。