继承
一.含义:
继承来自父类的成员(数据成员和成员函数)
二.访问规则:
父类成员变量在子类中的访问权限:在父类中的原有权限和继承方式中取最小即可。
min(父类中的原有权限,继承方式)
三.切片操作
子类为父类赋值(对象、引用、指针)
父类不能为子类赋值,除非强制类型转换,但是这样操作的结果就是会出现,访问到非法内存的情况。
四.菱形继承
1.菱形继承的结构如下:
代码表示如下:
//菱形继承
class A {
public:
int a;
};
class B1 :public A {
public:
int b1;
};
class B2 :public A{
public:
int b2;
};
class C :public B1, public B2 {
public:
int c;
};
但是,这样会出现数据冗余,与二义性的问题。
数据冗余:C的两个直接父类都存储了根本父类A的数据元素
二义性:通过继承关系,A的数据成员被继承了两份,通过C来访问其继承的A的数据元素时,会出现二义性,不清楚是B1继承的还是B2继承的。
看下面的代码:
void test2() {
C c;
c.B1::a = 40;
c.B2::a = 50;
c.b1 = 2;
c.b2 = 3;
c.c = 9;
}
当我们想修改C中继承的A的成员的值时,会出现二义性,修改一次后我们希望得到的结果应该是C继承体系中的a变量的值均被修改并且相同,但是呈现的结果却是这样的:
为了解决菱形继承的数据冗余和二义性的问题,我们引入虚拟继承来解决。
//菱形继承
class A {
public:
int a;
};
class B1 :virtual public A {
public:
int b1;
};
class B2 :virtual public A{
public:
int b2;
};
class C :public B1, public B2 {
public:
int c;
};
这样处理的结果是:
两种实现的原理:
这样看似空间比原来占用还大,但事实上,当根本父类A的成员变量多的时候,虚拟继承就体现出了它的空间优势。
五.派生类的默认成员函数:
class Person {
private:
int age;
public:
Person() {
cout << "Person()" << endl;
}
~Person() {
cout << "~Person()" << endl;
}
};
class Student :public Person {
private:
int sno;
public:
Student() {
cout << "Student()" << endl;
}
~Student() {
cout << "~Student()" << endl;
}
};
class Teacher :public Person {
private:
int tno;
public:
Teacher() {
cout << "Teacher()" << endl;
}
~Teacher() {
cout << "~Teacher()" << endl;
}
};
void test1() {
Person p;
Student s;
Teacher t;
}
函数调用情况如下:
构造与析构:派生类默认先调基类的构造函数,析构函数,先调用派生类的析构函数,再调用基类的析构函数。
拷贝构造:派生类有显示的拷贝构造,优先调用派生类的,不再调用基类的;否则,就自动调用基类的拷贝构造。
赋值运算符重载:调用规则与构造函数一致。
六.继承中的静态成员
当基类定义了一个静态成员时,该静态成员属于整个继承体系——它的任何一个派生类。