所以,我是一个c ++的新手,所以让我解释一下我的问题以及我如何理解它,如果我错了,请纠正我。
我有一个代表ui按钮的基类。它有onClick
,mouseOver
等方法和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
可以由其他类扩展,例如CheckboxButton
或SendButton
。您可以将每个按钮放在不同的(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 ++方法是什么?感谢
答案 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::width
和BaseButton::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
...
};
你看到有一个带有两个参数的短构造函数,它将构造展开为一个带有四个参数的构造函数,允许初始化height
和width
。请注意构造函数体中的语句在mem-initializers之后执行的方式。
派生类然后将调用适当的基础构造函数:
class CheckboxButton: public BaseButton {
public:
CheckboxButton (int x, int y) : BaseButton(x,y, 16,16) {}
};
如果width
和height
不是常量,那么您可以只为基类保留一个构造函数,就像在原始代码中一样,只是在派生构造函数的主体中分配了新值。