【问题标题】:Initialization list on derived-derived class on C++C ++中派生派生类的初始化列表
【发布时间】:2017-03-27 03:45:27
【问题描述】:

我不知道如何为派生派生类的构造函数实现初始化列表。请参阅基础抽象类:

public:
Shape(char *Type = "Unknown")       
{
    this->Type = Type;
}

string getType() {return Type;}

当然还有一个派生的Rectangle类,很容易实现:

public:
Rectangle(double Side1, double Side2) : Shape("Rectangle")
{
    this->Side1 = Side1;    
    this->Side2 = Side2;
}

然后我想导出 SquareRectangle 作为一个特例,但是对于我的生活,我无法弄清楚如何在下一个导出 @987654327 中包含假定的初始化列表 Shape("Square") @类:

public:
Square(double Size) : Rectangle(Size, Size) { }

我不能再次使用冒号运算符,可以吗?任何帮助,非常感谢。 我的最终目标是在 cpp 上查询两个派生类的类型,我将其实例化如下:

Shape *rectangle = new Rectangle(dSide1, dSide2);   
Shape *square = new Square(dSide1);     

并因此检索:

cout << setw(t) << shape[i]->getType().data() ;

【问题讨论】:

  • 只要去掉 Type 成员(无论如何存储 char* 是个坏主意)并将 getType 定义为虚函数:virtual string getType() { return "Unknown"; } - 并对所有类重复此操作
  • @Martin。不,我做不到:Square(double Size):Rectangle(Size, Size):Shape("Square){}...
  • @Ap31,+1 我刚刚尝试了您推荐的修改,当然它就像一个魅力,非常感谢。 char 指针实现是我们教授的想法,所以我不确定当他看到我更改了他的代码时它会如何下降:)
  • 如果这是您的教授告诉您的,那么您可能仅限于常规功能(我敢打赌虚拟继承也是不可能的)-在这种情况下,它看起来就像他可能正在为您准备引入虚拟功能:)

标签: c++


【解决方案1】:

除了@n.m.'s answer中的解决方案之外,另一种方法是将Type声明为受保护成员,并直接在子类的构造函数中对其进行初始化:

class Shape {
public:
    Shape()
    {
        Type = "Unknown";
    }

    string getType() { return Type; }

protected:
    string Type;
};

class Rectangle : public Shape
{
public:
    Rectangle(double Side1, double Side2)
    {
        this->Side1 = Side1;
        this->Side2 = Side2;
        Type = "Rectangle";
    }

protected:
    double Side1;
    double Side2;
};

class Square : public Rectangle
{
public:
    Square(double Size) : Rectangle(Size, Size) 
    { 
        Type = "Square";
    }
};

【讨论】:

  • 如果不允许使用虚函数和虚继承,这是最好的解决方案。请注意,虽然构造函数主体中的 assignment 不是 initialization。实际上,这将导致您的 Type 指针遍历继承路径中的所有值 - 例如当你构建一个正方形时,它会先是“未知”,然后它会变成“矩形”,然后是“正方形”——一些额外的工作,但它更安全,并且具有某种类型逻辑意义
  • 你说得对,但是Type也不能放在构造函数的初始列表中,有没有更好的办法?
  • 能否将Type 设为const 成员变量?
  • @MartinZhai 不,这就是重点 - 不需要更好的想法,因为这实际上是对象应该如何表现 - 这是自然且异常安全的。从技术上讲,vptr 也经历了相同的转换以使虚拟功能起作用。我什至会考虑在析构函数链中添加逆向过程:D(开个玩笑)
  • @Jonas 这有什么帮助?
【解决方案2】:

有两种方法。

  1. 使用虚拟继承。

    class Shape {
    public:
      Shape(char *Type = "Unknown")       
    
    class Rectangle: public virtual Shape {
    public:
      Rectangle(double Side1, double Side2) : Shape("Rectangle")
    
    class Square: public Rectangle, public virtual Shape {
    public:
      Square(double Size) : Rectangle(Size, Size), Shape ("Square") 
    
  2. 为所有构造函数添加类型参数:

    class Shape {
    public:
      Shape(char *Type = "Unknown")       
    
    class Rectangle: public Shape {
    public:
      Rectangle(double Side1, double Side2, char *Type = "Rectangle") : Shape(Type)
    
    class Square: public Rectangle {
    public:
      Square(double Size, char *Type = "Square") : Rectangle(Size, Size, Type)
    

不建议在您的类中使用类型字段(类型为 whartever 类型)。如果您需要人类可读的 tyoe 名称来显示,请使用虚函数

class Shape {
  // this should be an abstract class
  public: virtual const char* type() = 0;

class Rectangle : public Shape {
  public: virtual const char* type() {
    return "Rectangle";
  }

不要将这些函数用于其他事情,尤其是用于确定程序逻辑。

【讨论】:

  • 我喜欢虚拟继承,但设计一开始就有缺陷,这个修复只会让它变得更糟(因为它可以工作)
  • 感谢您抽出宝贵时间回答/评论。 @Ap31,您能指出我设计中的缺陷吗?我(显然)刚刚开始学习这种语言,非常感谢任何帮助。
  • 当然,我已经在 cmets 中提到了我认为该问题的预期解决方案;你所做的大部分都可以,但你不必将你的类名存储为成员字段——这是非常多余的。相反,当您需要多态行为时,只需使用 virtual 函数。无论如何,存储char* 也是一个主要的错误磁铁,因为它是一个非拥有指针
  • @n.m.我尝试了您的建议,当然,它完美无缺。非常感谢您花时间回复并帮助我更好地理解这种美丽的语言。
  • @n.m.我并不是说虚拟继承解决方案不好,实际上它可能比在所有public 构造函数中将Type 成员作为参数公开要好,这感觉就像是解决一个不存在的问题。这是间接的一层,但谁在乎呢。否则它很聪明:)
猜你喜欢
  • 1970-01-01
  • 2020-08-19
  • 2022-01-21
  • 1970-01-01
  • 1970-01-01
  • 2017-07-13
  • 2015-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多