基本语法
类继承的基本使用语法很简单:
class 子类名 : 继承方式 父类{};
继承方式分为public、protected、privated
class Car
{
public:
int a;
protected :
int b;
private:
int c;
};
//public继承后:父类私有属性私有属性不可访问,其他属性以原有的修饰符进行继承,及public成员继承后还是public,protected成员继承后还是protected属性的
class miniCar: public Car
{
public:
void visit()
{
a = 100;
b = 100;
c = 100;;//此行有问题,因为c是父类的私有属性,父类私有属性私有属性不可访问(其实private属性也是继承的,只是访问不了)
}
};
//在外部情况
miniCar mc1;
mc1.a=100;
mc1.b=100;//此行错误,b是protected属性,不能在外部访问
mc1.c=100;//此行错误,mc1对象里面没有c
//protected继承,父类私有属性私有属性不可访问,其他属性全变成protected类型的属性
//private继承,父类私有属性私有属性不可访问,其他属性全变成private类型的属性
对象创建的顺序
创建子类对象时,会先创建父类对象,调用父类对象的构造函数,再调用子类对象的构造函数,子类对象被销毁时,先调用子类的析构函数,再调用父类的析构函数
父类子类同名成员访问方法
普通函数同名
如果父类和子类包含了同名字的成员,需要注意,当没有同名的时候,子类对象直接调用的是从父类拿过来的成员,但是一旦子类有和父类名字一样的成员,子类对象直接调用时,会调用子类的成员而忽视父类的成员,不管是函数还是属性都是这样,此时想要调用父类的同名成员,需加作用域符号
class Car
{
public:
int a;
Car ()
{
a = 100;
}
void display()
{
cout<<"this is function of Car"<<endl;
}
};
//情况1:无重名
class miniCar : public Car
{
};
miniCar mc1;
cout<<mc1.a;
mc1.display();
//以上输出结果为 100 this is function of Car
//情况2:有重名
class miniCar : public Car
{
public:
int a;
miniCar ()
{
a = 200;
}
void display()
{
cout<<"this is function of miniCar"<<endl;
}
};
miniCar mc1;
cout<<mc1.a;
mc1.display();
//以上输出结果:200 this is function of miniCar
cout<<mc1.Car::a;
mc1.Car::display();
//以上输出结果为100 this is function of Car
//显然,当重名时,只需要在想调用父类对象属性时加上域符号即可
//当父类或者子类对象发生函数重载,且父类和子类发生函数重载的函数重名时
//需要注意,发生重名时如果不加类作用域符号,编译器只会在子类中寻找是否有符合条件的重载函数
class Car
{
public:
int a;
Car ()
{
a = 100;
}
void display()
{
cout<<"this is function of Car"<<endl;
}
void display(int a)
{
cout<<"this Car's number is"<<a<<endl;
}
};
class miniCar : public Car
{
public:
int a;
miniCar ()
{
a = 200;
}
void display()
{
cout<<"this is function of miniCar"<<endl;
}
};
miniCar mc2;
mc2.display(1);//此行代码错误,因为在子类中找不到符合类型的函数,需要去父类中找,加作用域即可
mc2.Car::display(1);//输出结果为this Car's number is 1
静态成员重名
静态成员重名时,无论是成员函数还是成员属性和普通函数都是相同的,只不过有两种调用方法,其中一个就是普通成员所采用的对象调用方法,还有一种方法如下
//假设Car和子类miniCar中都包含static void showName(){}这个函数
//调用子类的函数
miniCar::showName();
//调用父类的函数,其中第一个::表示是miniCar类下所包含的内容,
miniCar::Car::showName();
//第二个::表示调用父类作用域下的函数,距离函数最近的::表示的就是作用域,
//其实第一个::也可以看作作用域,整体意思就是,
//miniCar类作用域下的父类Car作用域下的showName函数
多继承
基本语法:class 子类:继承方式 父类1,继承方式 父类2,继承方式 父类3,…{};
多继承需要注意父类之间的重名问题,要加作用域符号
class Car1
{
public:
int num;
Car1()
{num=100;}
}
class Car2
{
public:
int num;
Car2()
{num1=200;}
}
class miniCar:public Car1,public Car2
{};
miniCar mc1;
show<<mc1.Car1::num<<endl;//输出100
show<<mc1.Car2::num<<endl;//输出200
菱形继承、虚继承
菱形继承指的是,比如一个Car被miniCar和bigCar继承了,又有一个类everyCar继承了miniCar和bigCar两个属性,这时,继承就会出现两个问题,一是,继承的属性重名的问题,这可以使用作用域符号解决,二是everyCar只需要继承过来的一份属性就够了
这时候,使用以下方案
//在miniCar和bigCar继承时使用虚继承,此时Car称为虚基类
class miniCar:virtual public Car{};
class bigCar:virtual public Car{};
从图中可以看出,当使用虚继承,miniCar继承的其实是一个虚基类指针,这个指针指向vbtable这个虚基类表格,表格里记录的是基类有哪些子类,以及该子类需要在虚指针外加多少偏移量才能访问到基类的属性
此时不论有多少个类使用虚继承来继承Car这个类,都不会真正继承num这个属性,这个属性可以说是所有虚继承的子类共同来维护的,可以理解为这个属性就是虚基类的,只不过给这些子类一起用
此时对于everyCar这个类来说,类的结构是以下这种(这里的多继承不是虚继承,只有miniCar和bigCar继承Car的时候用了虚继承)
可以看到,对于这个类来说,它继承了miniCar和bigCar的虚基类指针,而指针指向的列表里显示的地址偏移量都是用来让对应的虚基类指针来访问虚基类的属性的,0+8可以访问到num,4+4也可以访问到num,num是唯一的