Дополню камрада
ManWithBear
Не нужно структуру отдельно переделывать в класс. Это и так класс, только доступ по-умолчанию public. И метод проверки можно не добавлять, а сделать внешним по отношению к структуре. Свободные функции не всегда плохо. Плюсы такого подхода в том, что memory-layout будет предсказуемый, а если в структуре нет non-POD типов то они останутся POD типами.
Т.е. получится что-то вроде такого:
#include <iostream>
#include <iomanip>
using namespace std;
struct Point
{
int x;
int y;
};
inline bool is_valid(const Point& p)
{
bool result = true;
// some checks
return result;
}
struct Rect : Point
{
int width;
int height;
};
inline bool is_valid(const Rect& r)
{
bool result = is_valid(static_cast<const Point&>(r)) && r.width > 0 && r.height > 0;
return result;
}
int main() {
// теряется возможность делать так:
//Rect r = {0, 1, 2, 3};
Rect r{};
r.x = 0;
r.y = 1;
r.width = 10;
r.height = 11;
auto check = is_valid(r);
cout << " x=" << r.x
<< " y=" << r.y
<< " w=" << r.width
<< " h=" << r.height
<< " valid=" << check
<< endl;
cout << sizeof(Rect) << "/" << sizeof(int)*4 << endl;
uint8_t *ptr = reinterpret_cast<uint8_t*>(&r);
auto beg = ptr;
auto end = ptr + sizeof(Rect);
for (auto it = beg; it != end; ++it) {
cout << hex << setfill('0') << setw(2) << (int)*it << " ";
}
cout << endl;
return 0;
}
Можно функцию проверки сделать и методом класса/структуры и, при этом, не виртуальной. Тогда проверка будет вызываться только в соответствии с типом. Тогда можно обойтись без static_cast, получится что-то вроде:
#include <iostream>
#include <iomanip>
using namespace std;
struct Point
{
int x;
int y;
bool is_valid() const
{
bool result = true;
// some checks
return result;
}
};
struct Rect : Point
{
int width;
int height;
bool is_valid() const
{
bool result = Point::is_valid() && width > 0 && height > 0;
return result;
}
};
void pass_point(const Point &p)
{
cout << "point is valid: " << p.is_valid() << endl;
}
int main() {
// теряется возможность делать так:
//Rect r = {0, 1, 2, 3};
Rect r{};
r.x = 0;
r.y = 1;
r.width = 0; // make invalid
r.height = 11;
auto check = r.is_valid();
cout << " x=" << r.x
<< " y=" << r.y
<< " w=" << r.width
<< " h=" << r.height
<< " valid=" << check
<< endl;
// но следующий вызов скажет что точка валидна:
// вызов валиден, т.к. Rect отнаследован от Point
pass_point(r);
cout << sizeof(Rect) << "/" << sizeof(int)*4 << endl;
uint8_t *ptr = reinterpret_cast<uint8_t*>(&r);
auto beg = ptr;
auto end = ptr + sizeof(Rect);
for (auto it = beg; it != end; ++it) {
cout << hex << setfill('0') << setw(2) << (int)*it << " ";
}
cout << endl;
return 0;
}
Сложности могут возникнуть, когда появятся ромбические связи и потребуется виртуальное наследование.