【发布时间】:2018-05-21 19:40:49
【问题描述】:
简而言之:
B 类有一个指向 C 类的指针,C 类有一个 D 类有一个指向 B 类的指针
通过复制将 B 类分配给 A 类中的数组,期望看到 ptr 指向数组中的新实例而不是原始实例,但失败了。
我已经能够做一些解决方法,但我想知道为什么我原来的方法会失败。
更详细的解释如下,重现问题的代码也贴出来
感谢任何能够解释发生了什么的人。
有 6 个类:
class CastInfo //包含一个字符*
class Skill //抽象类,包含 CastInfo
类运动:公共技能
类 Move1:公共运动
class Character //包含一个Movement*,实际上是Move1*
class Squad //包含一个字符数组
具有以下关系:
CastInfo 中的Character* 应该指向拥有技能的角色 是 CastInfo 的所有者
将技能分配给角色时,CastInfo 中的角色* 指向该角色
Squad 数组中的 Character 应该被复制,所以会有 2 个实例,并且 CastInfo 中的 Character* 也应该指向 Squad 数组中的 Character,而不是原始实例
预期结果是:
move1 != ch1.move1 != square.ch[0].move1(这已经满足了)
ch1.move1->cast_info.caster == &ch1 != square.ch[0].move1->caster_info.caster(这是问题所在)
输出有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() 时)
在第二种情况下,我希望它首先调用复制构造函数,它触发 SetMove1(),它调用 SetCaster()。 move1 如图所示被克隆,但我不明白为什么 caster 没有正确更新。 (虽然是在构造之后调用operator=,但地址应该保持不变。)
以下代码应该会重现该问题:
运动.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 中的数组,并调用每个角色的 SetCaster() 方法。
void Squad::SetCaster() {
for (int i = 0; i < SQUAD_SIZE; i++) {
characters_[i].SetCaster();
}
}
但我认为这很脏,因为每次在 Builder::Builder() 之后,都必须调用 SetCaster(),这是不直观且容易出错的。
【问题讨论】:
-
您是否有机会将其缩减为更易于管理但仍能证明问题的东西?
-
说真的:
Character ch1 = Character::Builder().SetId(1).Build();这应该比Character ch1(1);更容易阅读 -
copy constructor, which triggers SetMove1()。如果编译器认为它可以进行优化,则可以省略复制构造函数(即使复制构造函数有副作用(它不应该))。 -
@Steve 我相信这里列出的所有成员和方法都已使用,除了类 Skill 和类 Move2,它们显示为表达技能系统的样子。
-
@LokiAstari 真的吗?我不知道编译器有没有可能通过它。但是,在这种情况下,它似乎没有通过,因为我在复制构造函数中设置了一个断点并且它正在工作。有什么关键词可以了解更多吗?
标签: c++ class pointers nested operator-overloading