所以,我是 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++ 方法是什么?谢谢
最佳答案
问题
在子项中,您尝试在不使用显式构造函数的情况下初始化父项的成员。在尝试这样做时,您在派生类中定义了与父类中的成员同名的新成员变量,以便隐藏它们的名称:
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。
解决方案
一个好的做法是在构造函数中使用内存初始化器构造成员变量,因为这些用于构造成员:
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
...
};
您看到有一个带两个参数的短构造函数,它将构造委托(delegate)给一个带四个参数的构造函数,它还允许初始化 height
和 width
。顺便注意,构造函数体中的语句是在内存初始化器之后执行的。
派生类然后将调用适当的基类构造函数:
class CheckboxButton: public BaseButton {
public:
CheckboxButton (int x, int y) : BaseButton(x,y, 16,16) {}
};
如果 width
和 height
不是常量,您可以只为基类保留一个构造函数,就像在您的原始代码中一样,并在派生构造函数的主体。
关于c++ - 如何在基类构造函数中使用派生类成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48240201/