如何在基类构造函数

时间:2018-01-13 13:13:01

标签: c++ oop inheritance

所以,我是一个c ++的新手,所以让我解释一下我的问题以及我如何理解它,如果我错了,请纠正我。

我有一个代表ui按钮的基类。它有onClickmouseOver等方法和label等成员。此外,它有一个rect成员,其中“矩形”的数据代表按钮:它在窗口中的x和y位置及其宽度和高度。

简化代码:

#include <cstdio> 

struct Rect {
  int x { 0 };
  int y { 0 };
  int w { 0 };
  int h { 0 };
};

class BaseButton {
  protected:
  const int width { 0 };
  const int height { 0 };
  Rect rect;

  public:
  BaseButton(int x, int y) {
    rect = Rect { x, y, width, height };
  }

  void debugRect () {
    printf("x: %d, y: %d, w: %d, h: %d\n", rect.x, rect.y, rect.w, rect.h);
  }
};

现在,BaseButton可以由其他类扩展,例如CheckboxButtonSendButton。您可以将每个按钮放在不同的(x,y)位置,但CheckboxButton的每个实例的宽度和高度都是16,而每个SendButton的高度都是16,宽度是32:

class CheckboxButton: public BaseButton {
  protected:
  const int width { 16 };
  const int height { 16 };

  public:
  using BaseButton::BaseButton;
};

class SendButton: public BaseButton {
  protected:
  const int width { 32 };
  const int height { 16 };

  public:
  using BaseButton::BaseButton;
};

如果我调试它,结果如下:

int main () {
  CheckboxButton cbb { 10, 10 };
  cbb.debugRect(); // x: 10, y: 10, w: 0, h: 0
}

与其他一些OOP语言的不同之处在于基类构造函数无法访问派生类变量。例如,在python中,代码将是这样的:

class Rect:
  def __init__(self, x, y, w, h):
    self.x = x
    self.y = y
    self.w = w
    self.h = h

class BaseButton:
  width = 0
  height = 0

  def __init__(self, x, y):
    self.rect = Rect(x, y, self.width, self.height)

  def debug_rect(self):
    print "x: {0}, y: {1}, w: {2}, h: {3}".format(self.rect.x, self.rect.y, self.rect.w, self.rect.h)

class CheckboxButton(BaseButton):
  width = 16
  height = 16

class SendButton(BaseButton):
  width = 32
  height = 16

cbb = CheckboxButton(10, 10)
cbb.debug_rect() # x: 10, y: 10, w: 16, h: 16

我想尽可能多地重用基类构造函数的代码。因此,基本上,“配置”派生类参数,并让构造函数代码使用它们来生成每种类型按钮的实例。

我能想到的一个解决方案是让基类构造函数接受它需要的所有参数,并使用固定参数从派生类中重载这样的构造函数,如下所示:

class BaseButton {
  Rect rect;

  public:
  BaseButton(int x, int y, int w, int h) {
    rect = Rect { x, y, w, h };
  }

  void debug_rect () {
    printf("x: %d, y: %d, w: %d, h: %d\n", rect.x, rect.y, rect.w, rect.h);
  }
};

class CheckboxButton: public BaseButton {
  private:
  using BaseButton::BaseButton;

  protected:

  public:
  CheckboxButton(int x, int y): BaseButton { x, y, 16, 16 } {};
};

int main () {
  CheckboxButton cbb { 10, 10 };
  cbb.debug_rect();
}

我能想到的另一个解决方案是使用模板:

template<int W, int H>
class BaseButton {
  Rect rect;

  public:
  BaseButton(int x, int y) {
    rect = Rect { x, y, W, H };
  }

  void debug_rect () {
    printf("x: %d, y: %d, w: %d, h: %d\n", rect.x, rect.y, rect.w, rect.h);
  }
};

class CheckboxButton: public BaseButton<16, 16> {
  public:
  using BaseButton::BaseButton;
};

int main () {
  CheckboxButton cbb { 10, 10 };
  cbb.debug_rect();
}

这两个解决方案有效,但我的问题是它们不能优雅地扩展。在我的简单示例中,我只是向基类构造函数添加两个参数,构造函数只是几行代码。如果配置参数是20并且构造函数比这复杂得多怎么办?

解决此类案例的标准c ++方法是什么?感谢

1 个答案:

答案 0 :(得分:1)

问题

在子级中,您尝试初始化父级成员而不使用显式构造函数。在尝试这样做时,您在派生类中定义新成员变量,其名称与父类中的成员相同,以便隐藏它们的名称:

class CheckboxButton: public BaseButton {
  protected:
  const int width { 16 };   // new CheckboxButton::width, 
                            //          leaves BaseButton::width unchanged
  const int height { 16 };  // leaves BaseButton::width unchanged
  ...
};

调试函数在基类中定义,只知道BaseButton::widthBaseButton::height,这些常量保持不变为0.

解决方案

一个好的做法是在构造函数中使用mem-initializer构造成员变量,因为它们用于构造成员:

class BaseButton {
protected:
  const int width;
  const int height;
  Rect rect;

public:
  BaseButton(int x, int y, int w, int h) : width{w}, height{h} {
    rect = Rect { x, y, width, height };
  }
  BaseButton(int x, int y) : BaseButton(x,y,0,0) {}  // delegate construction
                                                     // to other constructor

  ...
};

你看到有一个带有两个参数的短构造函数,它将构造展开为一个带有四个参数的构造函数,允许初始化heightwidth。请注意构造函数体中的语句在mem-initializers之后执行的方式。

派生类然后将调用适当的基础构造函数:

class CheckboxButton: public BaseButton {
public:
  CheckboxButton (int x, int y) : BaseButton(x,y, 16,16) {}
};

如果widthheight不是常量,那么您可以只为基类保留一个构造函数,就像在原始代码中一样,只是在派生构造函数的主体中分配了新值。