今天遇到一个继承的问题。请回答下面问题:(已知 B 继承自 A)
- 子类是否需要调用父类的构造函数?必须还是可选?
先回答:
- 结论1 :强烈建议显示调用父类构造。因为有些父类可能会初始化一些成员变量,子类又不知道他初始化了哪些变量。
- 结论2:写父类的时候仅写一个构造函数。写一堆构造方法,别人写子类的时候调用哪个呢?
展示一些野生程序员的父类,让我写子类的时候一堆坑。
例子1: 父类有成员未定义,建议你显示调用父类构造。
别人的父类:
class A
{
public:A(int data = -1): value1(data) {}int value1; // 正确初始的成员变量char * value2;// 未初始化的成员变量,你一旦用了就报错int value3;// 未初始化的成员变量,但有默认值0。
};
这种情况下,如果你的子类不调用,就会报错,而且是你的过错。
你的子类
class B : A
{
public:不建议的构造B(int data) : name(data) {cout << *value2 << endl; // 报错,调用野指针}建议的构造B(int data) : A(data), name(data) {cout << *value2 << endl; // 也会报错,但责任不在你} int name;
};
其实就是父类和子类各自管好各自的成员。
例子2 :父类的构造别定义一堆
有人喜欢这样定义父类:
class CRectangleData
{
public:CRectangleData();CRectangleData(const double &dStartPosX, const double &dStartPosY, const double &dWidth, const double &dHeight);private:double m_dStartPosX; //起点横坐标double m_dStartPosY; //起点纵坐标double m_dWidth; //宽度double m_dHeight; //高度
};
不好:
- 你有默认构造,子类就可以不用显示调用父类构造。我写子类的时候,到底调用你哪个父类构造呢?
- 哪些参数是必须的?哪些参数是可以设置的?哪些参数是默认的?没有体现出来。容易造成必须参数的未定义。
实际案例
项目:绘图程序
需求:绘制各种形状
- 父类:shape
- 子类:三角形、圆形等
先定义一些非必要的东西。
enum class ShapeType
{Shape_Unknown = 0, //未知形状Shape_Rectangle = 1, //矩形Shape_Ellipse = 2, //椭圆Shape_Triangle = 3, //三角形
};
不好的写法:
- 不好的1:父类、子类成员变量没有“初始化”,都是在构造函数中赋值;
- 不好的2:子类构造中应该体现:必须设置变量、可设置变量和不可设置变量三种
父类
class CShapeData
{
public:CShapeData(){m_ShapeType = ShapeType::Shape_Unknown; }
protected:ShapeType m_ShapeType;//形状类型
};子类椭圆形
class CEllipseData : public CShapeData
{
public:CEllipseData() // 不建议:无意义的定义{m_ShapeType = ShapeType::Shape_Ellipse;m_dStartPosX = 0.0;m_dStartPosY = 0.0;m_dRadiusW = 0.0;m_dRadiusH = 0.0;}CEllipseData(const double &dStartPosX, const double &dStartPosY, const double &dRadiusW, const double &dRadiusH){m_ShapeType = ShapeType::Shape_Ellipse;m_dStartPosX = dStartPosX;m_dStartPosY = dStartPosY;m_dRadiusW = dRadiusW;m_dRadiusH = dRadiusH;}private:double m_dStartPosX; //起点横坐标double m_dStartPosY; //起点纵坐标double m_dRadiusW; //横向半径长度double m_dRadiusH; //纵向半径长度
};
修改如下:
父类
class CShapeData
{
public:CShapeData(ShapeType shape):m_ShapeType(shape) // 修改地方1 : a) 使用初始化列表 b) 必须设定 m_ShapeType 值{}
protected:ShapeType m_ShapeType;
};子类椭圆形
class CEllipseData : public CShapeData
{
public:// 修改地方2:a) 唯一的构造 b) 父类、子类各自初始化自己的变量CEllipseData(const double &dStartPosX, const double &dStartPosY, const double &dRadiusW, const double &dRadiusH) : CShapeData(ShapeType::Shape_Ellipse) ,m_dStartPosX (dStartPosX),m_dStartPosY (dStartPosY),m_dRadiusW (dRadiusW),m_dRadiusH (dRadiusH){}private:double m_dStartPosX; //起点横坐标double m_dStartPosY; //起点纵坐标double m_dRadiusW; //横向半径长度double m_dRadiusH; //纵向半径长度
};