先摘一段别人的:
构造方法用来初始化类的对象,与父类的其它成员不同,它不能被子类继承(子类可以继承父类所有的成员变量和成员方法,但不继承父类的构造方法)。因此,在创建子类对象时,为了初始化从父类继承来的数据成员,系统需要调用其父类的构造方法。
如果没有显式的构造函数,编译器会给一个默认的构造函数,并且该默认的构造函数仅仅在没有显式地声明构造函数情况下创建。
构造原则如下:
1,如果子类没有定义构造方法,则调用父类的无参数的构造方法。
2,在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类自己提供了无参构造函数,则会调用父类自己的无参构造函数。
3,在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类只定义了自己的有参构造函数,则会出错(如果父类只有有参数的构造方法,则子类必须显示调用此带参构造方法)。
4,如果子类调用父类带参数的构造方法,需要用初始化父类成员对象的方式。
————————————————
版权声明:本文为CSDN博主「低头走路,抬头看天」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:
先放结论:以上原则,都是对的,但也可以违背。本文先顺着这些原则举例,
我的部分:
以上原则比较抽象,自己动手写写才能体会。在PUBG中,Scar属于AR(自动步枪)的一种,此为案例代码背景。
一,构造原则1:
如果子类没有定义构造方法,则调用父类的无参数的构造方法。
#include <iostream>
using namespace std;
class ar
{
public: //不写public会报无法访问private的错误
ar() //父类有无参数构造方法
{
cout << "来了支自动步枪" << endl;
}
};
class scar :public ar
{ //子类没有定义构造方法
};
int main()
{
ar Alice;
scar Bob;
return 0;
}
运行结果为:
来了支自动步枪
来了支自动步枪
可见父类的无参数构造函数在创建子类时被调用了。
需要注意的是,如果在实例后加了圆括号:
ar Alice();
scar Bob();
则运行结果为空,但是也不报错
二,构造原则2:
在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类自己提供了无参构造函数,则会调用父类自己的无参构造函数。
#include <iostream>
using namespace std;
class ar
{
public:
ar() //父类有无参数构造方法
{
cout << "来了支自动步枪" << endl;
}
};
class scar :public ar
{
public:
scar() //子类有无参数构造方法
{
cout << "来了把Scar" << endl;
}
};
int main()
{
ar Alice;
scar Bob;
return 0;
}
运行结果为:
来了支自动步枪
来了支自动步枪
来了把Scar
可见,虽然子类定义了构造函数,但是在创建子类时,仍然会调用父类的构造函数。
三,构造原则3:
在创建子类对象时候,如果子类的构造函数没有显示调用父类的构造函数且父类只定义了自己的有参构造函数,则会出错(如果父类只有有参数的构造方法,则子类必须显示调用此带参构造方法)。
#include <iostream>
using namespace std;
class ar
{
public:
ar(int dmg) //父类有带参数的构造方法
{
cout << "来了支自动步枪,单发伤害为"<<dmg << endl;
}
};
class scar :public ar
{
public:
scar() //子类还是无参数的构造方法
{
cout << "来了把Scar" << endl;
}
};
int main()
{
ar Alice(45);
scar Bob;
return 0;
}
运行报错:
到这儿基本明白了,无论子类的构造函数是啥样,创建子类必定调用父类的构造函数。所以父类的构造函数需要参数,子类却没法给,就得报错了。
如果这样写行不行呢:↓
class scar :public ar
{
public:
scar(int dmg) //子类的构造方法带了参数,但没显式调用父类的构造方法
{
cout << "来了把Scar" << endl;
}
};
依然是报错。
如果子类没有构造方法呢?还是报错。
正确写法是:
#include <iostream>
using namespace std;
class ar
{
public:
ar(int dmg) //父类有带参数的构造方法
{
cout << "来了支自动步枪,单发伤害为"<<dmg << endl;
}
};
class scar :public ar
{
public:
scar(int dmg):ar(dmg) //子类构造方法带了参数,且显式调用父类的构造方法
{
cout << "来了把Scar,单发伤害是"<<dmg << endl;
}
};
int main()
{
ar Alice(45);
scar Bob(42);
return 0;
}
运行结果为:
来了支自动步枪,单发伤害为45
来了支自动步枪,单发伤害为42 //注意这里是42而不是45哦
来了把Scar,单发伤害是42
四,构造原则4:
如果子类调用父类带参数的构造方法,需要用初始化父类成员对象的方式。
#include <iostream>
using namespace std;
class ar
{
public:
ar(int dmg) //父类有带参数的构造方法
{
cout << "来了支自动步枪,单发伤害为"<<dmg << endl;
}
};
class scar :public ar
{
public:
scar(int dmg):ar(dmg) //子类构造方法带参数,且显式调用父类的构造方法
{
cout << "来了把Scar,单发伤害是"<<dmg << endl;
ar(61); //子类调用父类带参数的构造方法,需要用初始化父类成员对象的方式,且只能在这里调用
}
};
int main()
{
ar Alice(45);
scar Bob(42);
return 0;
}
运行结果:
来了支自动步枪,单发伤害为45
来了支自动步枪,单发伤害为42
来了把Scar,单发伤害是42
来了支自动步枪,单发伤害为61
需要注意的,ar(61);
这个调用只能写在子类的构造函数定义中。
BTW,什么自动步枪的单发伤害能有61?当然是MK14-EBR。
五,反转来了
上面几个例子看下来,似乎有了这么个表:
父类构造函数的参数数量 | 子类构造函数的参数数量要求 |
无 | 没要求 |
N(N>0) | ≥N且包含父类的所有构造函数参数 |
真的是这样吗?
其实还有这么个骚操作:
#include <iostream>
using namespace std;
class ar
{
public:
ar(int dmg, int bullet) //父类有带2个参数的构造方法
{
cout << "来了支自动步枪,单发伤害为" << dmg;
cout<<",一个弹匣有"<<bullet<<"发子弹"<< endl;
}
};
class scar :public ar
{
public:
scar(int dmg); //声明子类的构造方法,它只带1个参数,可以吗?
};
scar::scar(int dmg) :ar(dmg, 30) //定义子类构造方法,调用父类的构造方法时把另一个参数赋值
{
cout << "来了把Scar,单发伤害是" << dmg << endl;
}
int main()
{
ar Alice(61, 20);
scar Bob(42);
return 0;
运行结果为:
来了支自动步枪,单发伤害为61,一个弹匣有20发子弹
来了支自动步枪,单发伤害为42,一个弹匣有30发子弹
来了把Scar,单发伤害是42
可见,在为了构建子类的构造函数而调用父类的构造函数时,给父类里那些子类不用的参数赋个值就OK了。
所以哪怕父类的构造函数有100000个参数,子类的构造函数其实依然可以是无参数的。
六,搞复杂点
子类可以直接调用父类的成员属性。
子类定义构造函数时,还可以给自己的成员变量赋默认值:
#include <iostream>
using namespace std;
class ar
{
public:
ar(int dmg,int bullet) //父类有带2个参数的构造方法
{
cout << "来了支自动步枪,单发伤害为"<<dmg << endl;
}
int v0=880; //父类设一个成员属性“子弹初速”
};
class scar :public ar
{
public:
scar(int dmg); //声明子类的构造方法,它只带1个参数哦,可以吗?
float caliber; //子类设置个成员属性“口径”
};
scar::scar(int dmg) :ar(dmg, 30) //定义子类构造方法,显式调用父类的构造方法,没用的父类构造参数给了个值“30”
,caliber(5.56) //在这儿可以设置子类的成员属性的默认值。
{
cout << "来了把Scar,单发伤害是" << dmg <<",";
cout << "口径是" << caliber <<"mm"<< endl;
}
int main()
{
ar Alice(61,20);
scar Bob(42,30);
Bob.v0 = 870; //修改子类从父类继承的“子弹初速”属性
cout << "Bob手里的枪子弹初速为" << Bob.v0 << "m/s" << endl;
return 0;
}
运行结果为:
来了支自动步枪,单发伤害为61
来了支自动步枪,单发伤害为42
来了把Scar,单发伤害是42,口径是5.56mm
Bob手里的枪子弹初速为870m/s