【问题标题】:C++ How to create copy constructor of derived class without using setter/getterC ++如何在不使用setter/getter的情况下创建派生类的复制构造函数
【发布时间】:2017-11-28 21:33:40
【问题描述】:

我有一个小问题。我有这些课程:

class Virus
{
private:
    char* DNA;
    int resistance;
};

class FluVirus : public Virus 
{
private:
    char* type;
};

class HIVVirus : public Virus 
{
private:
    int* undefinedBehavior;
};

场景是我有类 Patient。患者已感染病毒。有时,病毒会自我复制并使患者变得更弱。所以它可以成为新的目标病毒,初始变量与父病毒相同。 (无关的事情是obj患者可以使用药物降低固定数量的病毒强度,在全部死亡后恢复正常)

现在,我想创建派生类和基类的复制构造函数。问题是:

  1. 如果基类即Virus的指针变量(动态分配的数据)是public的,有人说它违反了封装性。

  2. 如果指针变量被保护,派生类可以访问它,但还是违反了封装性。

  3. 如果指针变量是私有的,但基类具有受保护的 getter 和 setter 方法,他们会说“Getter 和 setter 是您的类没有以有用的方式设计的标志”。

    李>

那么,如果我需要一个拷贝构造函数,我是否违反了封装,我的设计有很大的问题?

是否有任何可用的解决方案可以在不违反这些问题的情况下在派生类中创建复制构造函数?


澄清一下,如果我想让外人不知道病毒的 DNA,我需要保密。如果我将其设为受保护,则某人或我可以创建一个派生自 Virus 的类,并复制和更改病毒的信息 (DNA)。他们说这违反了封装,因为我做了一个后门来泄露信息。

如果我不使 DNA 受到保护,并且我想复制,我无法复制,因为派生类对基类的访问被拒绝。那么,是否有任何可能的解决方案可以避免受保护但仍然可以制作副本?

【问题讨论】:

  • 是否要将基类的数据复制到派生类的数据中?
  • 你的意思是派生类?
  • 是的,我想从其他派生类中复制一个派生类,但只有基变量相同时它们才相同。
  • 那为什么不在派生类的拷贝构造函数中调用基类的拷贝构造函数呢?
  • 还有一个问题:为什么你认为派生类可以访问m_base(受保护的成员)是违反封装的?

标签: c++ pointers getter-setter


【解决方案1】:

为什么不简单:

class Virus
{
public:
    virtual ~Virus() = default;
    virtual Virus* Clone() const = 0;
protected:
    Virus(const Virus&) = default;
private:
    std::vector<char> DNA;
    int resistance;
};

class FluVirus : public Virus 
{
public:
    FluVirus* Clone() const override { return FluVirus(*this); }
protected:
    FluVirus (const FluVirus&) = default;

private:
    std::vector<char> type;
};

class HIVVirus : public Virus 
{
public:
    HIVVirus* Clone() const override { return HIVVirus(*this); }
protected:
    HIVVirus(const HIVVirus&) = default;

private:
    std::vector<int> undefinedBehavior;
};

【讨论】:

    【解决方案2】:

    这是我朋友的回答,我想它可能对我有帮助。所以我贴出来。

    良好的软件设计倾向于组合而不是继承。

    https://en.wikipedia.org/wiki/Composition_over_inheritance

    原理是:

    1) 抽象不应该知道实现

    2) 基类不应该知道派生类

    3) 实现当然要了解抽象

    所以派生类知道信息基类是正常的,但是信息需要抽象,抽象是公开的,而不是隐藏的。

    所以你的问题是你需要在基类中隐藏你不应该有这种需要的东西。

    --

    简单的意思是如果我需要隐藏信息,我需要将信息存储在交付的类中。为常用功能制作抽象类/接口。 所以,而不是

    class Virus
    {
    private:
        char* DNA;
        int resistance;
    public:
        Virus* clone();
    };
    
    class FluVirus : public Virus 
    {
    private:
        char* type;
    };
    
    class HIVVirus : public Virus 
    {
    private:
        int* undefinedBehavior;
    };
    

    我们可以将私有信息移到派生类中

    class Virus
    {
    
    public:
        Virus* clone();
    };
    
    class FluVirus : public Virus 
    {
    private:
        char* DNA;
        int resistance;
    
        char* type;
    };
    
    class HIVVirus : public Virus 
    {
    private:
    
        char* DNA;
        int resistance;
    
        int* undefinedBehavior;
    };
    

    protected 不安全,是一种泄漏信息。

    【讨论】:

      【解决方案3】:

      也许,我不了解您的任务限制,但您似乎在使用继承时对理解protected 数据感到困惑。通常,继承允许您扩展功能,因此使用基类(受保护的成员)的数据并不违反封装 - 实际上这是封装的使用。例如。你有 Rectangle 的基类存储大小(高度和宽度或顶点坐标 - 左上角和右下角 X 和 Y),然后你想创建派生类阴影矩形并为填充(阴影)添加颜色 - 很明显,矩形已经有大小数据,唯一需要的附加数据是颜色。但是,如果您的目标是使用 Rectangle 的大小创建类似 House foundation(另一个类)的东西,则有可能不需要继承 - 只需将 public size getter 添加到 Rectangle 并使用它。

      我知道,对于您的情况,您只需要一个位置来存放 private int* 数据 - 基类:

      class Base
      {
      public:
          Base()
          {
              m_base = new int(42); // let it be just int in the dynamic memory
          }
          Base(const Base &b)
          {
              m_base = new int(*b.m_base);
          }
      protected:
          int* m_base;
      };
      

      只有在Base 类中才需要复制构造函数,因为如果m_baseprotectedDerived 将具有相同的数据。

      只是一个简单的说明:

      #include <iostream>
      
      class Base
      {
      public:
          Base()
          {
              m_base = new int(42);
              std::cout << "Base default" << std::endl;
          }
          Base(const Base &b)
          {
              m_base = new int(*b.m_base + 1); // +1 just to see the different value
              std::cout << "Base copy" << std::endl;
          }
      protected:
          int* m_base;
      };
      
      class Derived : public Base
      {
      public:
          void Show()
          {
              std::cout << *m_base << std::endl;
          }
      };
      
      int main()
      {
          Derived  x1; // Default constructor allocate memory and save 42 to it
          x1.Show();
          Derived  x2(x1); // copy constructor of Base class is used
          x2.Show();
          return 0;
      }
      

      更新:

      最后,当您需要从 Base 创建 Derived 时,例如:

      int main()
      {
          Base  x1;
          Derived  x2(x1);
          x2.Show();
          return 0;
      }
      

      您唯一需要的是向Derived 添加一个构造函数(不需要数据private int* m_derived;):

      class Derived : public Base
      {
      public:
          Derived(const Base &b) : Base(b)
          {
              std::cout << "Derived \"copy\" from Base" << std::endl;
          }
          void Show()
          {
              std::cout << *m_base << std::endl;
          }
      };
      

      【讨论】:

      • 感谢您的评论。我确实简化了我的设计。我有一个带 DNA 的病毒类(指针),还有流感病毒、艾滋病病毒等。每种病毒都有不同类型的变量(指针)。例如,FluVirus 有 char* 名称(FluVirus 的名称),...问题是当我想复制 FluVirus 时。如果我使用复制构造函数,我需要访问病毒的 DNA(基类)。如果它受到保护,那就没问题了。但是,也有人说这可能违反封装,使用受保护的变量是一个糟糕的设计。
      • @ThangNguyenXuan 想想你的所有病毒都需要基类,也许你不需要基类中的数据,只需要声明所有病毒通用的一些方法,在这种情况下是基类类扮演接口的角色,根本没有数据——数据是在每个特定的类中定义的,但在这种情况下,你肯定需要一些 getter(基类的公共或受保护接口中的方法)病毒使用其他人的数据(注意 - 在这种情况下其他人不是基类,但它们也是同一个基类的派生类)
      • 是的,它可以是一个解决方案。将 Virus aka 基类变成抽象类,然后在真正的类中做事。但是,例如,是否有任何其他解决方案,因为我认为在基类中制作相同的类信息是一种在 OOP 中保存代码(?)
      猜你喜欢
      • 2022-07-23
      • 1970-01-01
      • 2011-11-23
      • 2019-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-23
      • 1970-01-01
      相关资源
      最近更新 更多