1、继承的语法
我们把被继承的类称为基类或者父类,继承父类的称为派生类或者子类。
他的语法是 class 子类:继承方式 父类1,父类2…{};
2、继承方式
继承方式和类中的权限一样,有public,protected,private三种
class Base1{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
1、public继承 子类可以访问父类中的公共成员和保护成员,但不可访问私有成员,子类中继承的成员的权限和父类相同。
class son1:public Base1{
public:
void func(){
m_A=10;
m_B=10;
// m_C=10;
// cout<<m_C<<endl; 错误 m_C是私有权限
}
};
2、protected继承 子类可以访问父类中的公共成员和保护成员,不可以访问私有成员,子类中继承的成员的中公共成员变为保护成员。
class son2:protected Base1{
public:
void func(){
m_A=10;
m_B=10;
}
};
3、private继承 子类可以访问父类中的公共成员和保护成员,不可以访问私有成员,子类中继承的成员的公共成员和保护成员变为私有成员,如果它也有子类成员,它的子类成员将不可以访问父类的继承的成员。
class son3:private Base1{
public:
void func(){
m_A=10;
m_B=10;
}
};
父类中的所有成员对象都会被子类继承,只是父类中的私有成员被隐藏了。
3、继承中的析构和构造函数
1、调用顺序
class Base{
public:
Base(){
cout<<"Base构造函数"<<endl;
}
~Base(){
cout<<"Base析构函数"<<endl;
}
};
class Son:public Base{
public:
Son(){
cout<<"Son构造函数"<<endl;
}
~Son(){
cout<<"Son析构函数"<<endl;
}
};
结果为
显而易见构造函数是先调用父类的构造函数,再调用子类的构造函数,析构函数是先调用子类的析构函数再调用父类的析构函数。
2、构造函数注意的地方
我们要知道,子类是不会继承父类的构造函数的,如果父类写了默认构造函数,那么子类可以不写父类的构造函数,但是如果父类写有参构造函数或者拷贝构造函数,子类是必须重写至少一个的,否则将会出错。
class square :public shape
{
public:
int m_a;
int p;
square(int i):m_a(i){p=i;}
int area(){
return m_a*m_a*a;
}
};
class cube :public square
{
public:
int m_b;
cube(int j):square(p){m_b=j;};
int area(){
return m_b*m_b*b;
}
int volume(){
return m_b*m_b*m_b;
}
};
由于博主经验尚浅所以只能写出这样的例子,这还是我考试时候的一个题呢。我们发现在cube类中我写了这样的一个构造函数 cube(int j):square(p){m_b=j;};emmm没错,子类就是这样重写父类的构造函数的即 子类类名(参数):父类类名(初始值){};
构造函数大概就是这些了。欢迎补充。
4、继承中的同名静态成员处理
虽然同名,但却是在不同作用域下的局部静态成员,所以我们可以通过一些方法来访问:
1、通过创建对象来访问
对于子类可以直接 对象名. 来访问,而访问父类的则需要 对象名.父类类名::静态成员名 来访问。
2、通过类名直接访问
我们知道静态成员是可以通过类名来直接访问的。
访问子类 子类类名::静态成员名
访问父类 子类类名::父类类名::静态成员名,其中第一个::代表通过类名的方式访问,第二个::代表通过作用域访问,即通过子类访问父类作用域下面的静态成员。
5、菱形继承
菱形继承就是两个子类同时继承了同一个父类,又有一个子类(我们暂且叫他孙子类)同时继承了前两个子类,这样的继承图像一个菱形。但菱形继承可能存在一些问题,就是我可能不需要前两个子类的所有数据,我只需要继承两个中间的一个就行。或者孙子类的对象同时修改了两个子类中共有的变量,那么编译器是不知道修改了哪一个的(亲测)。怎么办呢,我们利用虚继承来解决这个问题。
虚继承就是在继承方式前添加virtual关键字。
当子类继承父类时,会产生一个虚基类指针-vbptr,子类将继承这个指针而不是数据,而且似乎两个父类共用这个指针,只是父类继承的数据的内存地址不一样,指针通过偏移量找到这个数据。如果同时修改两个数据,那么虚基类指针将会指向最后一次修改的数据的内存空间,这样就不会出现模棱两可的错误。