【问题标题】:Copy constructor between derived classes在派生类之间复制构造函数
【发布时间】:2011-10-10 15:40:37
【问题描述】:

如何将派生类复制到另一个?

我的术语有缺陷,所以我将尝试用一个例子来说明。

我们正在与计算机玩家和人类玩家一起玩纸牌游戏。 Card 和 Command 是其他类。

class Player
{
    Card *Hand[4];
    // etc...
};

class Human: public Player
{
    Command getCommand();
    void PlayCard(Card card);
    void quit();
    // etc...
};

class Computer: public Player
{
    Command ai();
    void PlayCard(Card card);
    // etc...
};

我们在主函数的某个地方...

// ...
Human p1; // Assume initialized and usable.
if(p1.getCommand() == QUIT)
{
    cout << "PLAYER 1 RAGEQUITS WHAT A NOOB LOL << endl;
    cout << "A COMPUTER WILL NOW TAKE OVER." << endl;
    p1.quit()
    p1 = new Computer(); // THE IDEA BEING THAT WE WANT TO PRESERVE p1's MEMBERS.
}
// ...

我要做的是将 p1 转换为“计算机”,同时保留其成员的状态。

我们是否使用复制构造函数来执行此操作?如果没有,你使用什么方法?

编辑:这是使用赋值运算符的方式吗?

Computer& Human::operator=(const Human &h) // Assignment operator
{
    Hand = h.Hand;
    member2 = h.member2;
    member3 = h.member3;
    ...

    return *this;
}

我们需要删除/释放主要内容吗?

【问题讨论】:

  • 好消息:你拼写“术语”正确:)
  • 这是class,不是Class;和public,而不是Public

标签: c++ oop abstract-class copy-constructor


【解决方案1】:

你可以使用构造函数。对于这种特殊情况,我可能会有一个名为“CopyGameState”之类的方法。

【讨论】:

    【解决方案2】:

    复制构造函数用于将现有对象复制到同一类的新实例。

    您需要的是一个赋值运算符 (operator=),它允许将不同类型的值分配给您的现有类对象。

    【讨论】:

    • 这是完全倒退的,因为它不会替换 vtable。
    • @Ben - 你不能替换构造类的 vtable。
    • 在没有创建新对象的情况下,这就是为什么赋值运算符在这里不是一个好的解决方案的原因。他需要一个带有新 vtable 的新对象。
    • @Ben - 他需要重新设计。直到我读了彼得的回答之后,我才清楚地理解真正的问题是什么。
    • 他希望p1-&gt;PlayCard() 开始使用Computer::PlayCard 而不是Human::PlayCard,同时保持成员数据不变。 @Peter 的回答或我的回答都会让他到达那里。
    【解决方案3】:

    它不是复制构造函数,而是转换构造函数。比如:

    Computer::Computer(const Player& had_to_go) : Player(had_to_go) {}
    

    这将使用Player 的复制构造函数来保留公共基类中的成员。

    当然,你最好让Player::Player(const Player&amp;) 正常工作,跟在"rule of three" 之后。

    最后,你会做这样的事情:

    p1.quit();
    Computer* replacement = new Computer(p1);
    delete p1;
    p1 = replacement;
    

    【讨论】:

    • 你能向我解释一下什么是“三法则”吗:P,还有,Player::Player(const Player&) Player 的复制构造函数是什么?
    • @Ir Win:是的,它是Player 的复制构造函数,三法则说如果你有一个自定义的复制构造函数、赋值运算符或析构函数,你需要这三个。在这种情况下,这是因为Card* 存储在Player 中。编译器提供的副本将复制指针,这意味着您最终将删除它两次。不好。 C++-FAQ 标签中有很好的解释。我将从我的答案中添加一个链接。
    【解决方案4】:

    您在这里遇到了设计问题。如果您想在保留公共成员变量的同时将玩家从人类切换到计算机,那么您应该以这种方式构建您的类。

    class Player
    { 
    public:
        friend class Human;    // These friends are necessary if the controllers
        friend class Computer; // need access to Player's private data.
    
        Card hand[4];
        Controller* controller;
    };
    
    class Controller
    {
    public:
        virtual Command getCommand(Player const&) = 0;
    };
    
    class Human : public Controller
    {
    public:
        Command getCommand(Player const&) { /* get command from user input */ }
    };
    
    class Computer : public Controller
    {
    public:
        Command getCommand(Player const&) { /* get command from AI */ }
    };
    

    然后,当你需要从人机切换到计算机时,只需更换控制器即可。

    p1->controller = new Computer();
    

    这样一来,卡片将被保留,只是控制机制会改变。

    【讨论】:

    • 这是另一种合理的方法,尽管Controller 可能需要以某种方式读取Player 的私有数据。
    • 在这种情况下,玩家可以公开该数据,或者您可以让控制器成为玩家的朋友。这就是朋友的目的:)
    • 这不仅仅是访问说明符的问题,还需要指向实例的指针。但这也很容易解决。
    • @Ben Voigt:是的,刚刚意识到这就是你的意思。我已将 Player ref 添加到 getCommand 调用:-)
    • 只需添加一个播放器上下文,并将一个不可变的引用传递给控制器​​。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-07
    • 1970-01-01
    • 2013-06-23
    • 1970-01-01
    • 1970-01-01
    • 2018-09-24
    • 1970-01-01
    相关资源
    最近更新 更多