So, I'm kind of a newbie in c++, so let me explain my problem and how I understand it and please correct me if I'm wrong.
I have a base class that represents ui buttons. It has methods like onClick
, mouseOver
and members like label
. Also, it has a rect
member with the data of the "rectangle" representing the button: its x and y position in the window and its width and height.
Simplified code:
#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);
}
};
Now, BaseButton
can be extended by other classes, like CheckboxButton
or SendButton
. You can place each button at a different (x, y) position, but every instance of CheckboxButton
has a width and height of 16, while every SendButton has a height of 16 and a width of 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;
};
If I debug it, the result is this:
int main () {
CheckboxButton cbb { 10, 10 };
cbb.debugRect(); // x: 10, y: 10, w: 0, h: 0
}
The difference from what happens in some other OOP languages is that the base class constructor has no access to the derived class variables. In python, for instance, the code would be like this:
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
I would like to reuse the base class constructor's code as much as I can. So, basically, "configure" the derived class parameters and have the constructor code use them to make the instances of each type of button.
One solution I can think of it to have the base class constructor accept all the parameters it needs and overload such constructor from the derived classes with fixed parameters, like this:
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();
}
Another solution I can think of is using templates:
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();
}
These two solutions work, but my problem is that they don't scale elegantly. In my simplistic example, I'm just adding two arguments to the base class constructor and the constructor is just a couple of lines of code. What if the configuration parameters are 20 and the constructor is much more complicated than this?
What is the standard c++ way of solving cases like this? Thanks