目录
继承的概念
继承定义
继承基类成员访问方式
基类和派生类对象的赋值转换
继承中的作用域
派生类的默认成员函数
友元关系不能继承
基类static成员
菱形继承与菱形虚拟继承
虚拟继承解决数据冗余和二义性的原理
继承和组合
继承是类层次的复用。
// 派生类 继承方式 基类
struct Student : public Person
{};
继承的使用
class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "perter";int _age = 18;private:int _aa;
};//继承后父类的成员(成员函数+成员变量)都会变成子类的一部分
struct Student :public Person
{
public:void f(){//_aa = 1 // 不可见}
protected:int _stuid;
};class Teacher :public Person
{
protected:int _jobid;
};
public继承 | protected继承 | private继承 | |
基类的public成员 | 派生类的public成员 | 派生类的protected成员 | 派生类的private成员 |
基类的protected成员 | 派生类的protected成员 | 派生类的protected成员 | 派生类的private成员 |
基类的private成员 | 在派生类中不可见 | 在派生类中不可见 | 在派生类中不可见 |
总结:
1.基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
2.如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。
3.基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。
class Person
{
protected:string _name; string _sex;
public:void f(){_age = 1;}
private:int _age;
};class Student : public Person
{
public:int _No;
};int main()
{Person p;Student s;//父类 = 子类 赋值兼容 -> 切割 切片//这里不存在类型转换,是语法天然支持行为p = s;Person* ptr = &s;ptr->f();//ptr->_age = 1;Person& ref = s;//子类 = 父类//s = (Student)p; // 不行Student* pptr = (Student*)&p;Student& rref = (Student&)p;//很危险的,存在越界访问的风险//pptr->_No = 1;return 0;
}
//A 和 B的func构成函数隐藏
class A
{
public:void fun(){cout << "func()" << endl;}
};class B :public A
{
public:void fun(int i){cout << "func(int i)->" << i << endl;}
};void Test()
{B b;b.fun(1); //b.fun() //被隐藏了,所以调不动b.A::fun();
}
class Person
{
public:Person(const char* name = " hh") //没有默认构造函数的话派生类就要写了:_name(name){cout << "Person()" << endl;}Person(const Person& p):_name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;//delete[] _ptr;}protected:string _name;//int* _ptr = new int[10];
};class Student : public Person
{
public:Student(const char* name = "张三", int num = 1):Person(name), _num(num){}//s2(s1)Student(const Student& s):Person(s), _num(s._num){}//s2 = s1Student& operator=(const Student& s){if (this != &s){Person::operator = (s);_num = s._num;}return *this;}//析构函数名字会被统一处理成destructor()//所以子类的析构函数跟父类的析构函数就构成隐藏~Student(){//Person::~Person();//delete[] _ptr;}//子类析构函数结束时,会自动调用父类的析构函数//不需要显示调用父类析构函数//这样才能保证先析构子类成员,再析构父类成员protected:int _num = 1;//string _s = "hellllllllllllllllllllllllllo";//int* _ptr = new int[10];
};//不写,编译器默认生成的派生的构造、析构和operator=
//父类继承下来的:调用父类默认构造和析构、operator= 处理 自己的:跟普通类一样// 什么情况下自己写?
// 1、父类没有默认构造,需要我们自己显示写构造
// 2、如果子类有资源需要释放,就需要自己显示写析构
// 3、如果子类存在浅拷贝问题,就需要自己实现拷贝构造和赋值解决浅拷贝问题// 如果我们要自己写怎么办?如何写?
// 父类成员调用父类的对应构造、拷贝构造、operator=和析构处理
// 自己成员按普通类处理。int main()
{Student s1;Student s2(s1);//Student s3("jack", 18);//s1 = s3;return 0;
}
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};
class Student : public Person
{
protected:int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}
void main()
{Person p;Student s;Display(p, s);
}
class Person
{
public:Person(){++_count;}protected:string _name;
public:static int _count;
};int Person::_count = 0;class Student :public Person
{
protected:int _stuNum;
};class Graduate :public Student
{
protected:string _seminarCourse;
};int main()
{Person p;Student s;Graduate g;cout << Person::_count << endl;cout << Student::_count << endl;cout << Graduate::_count << endl;return 0;
}
单继承:一个子类只有一个直接父类
多继承:一个子类有两个或以上直接父类
菱形继承:是多继承的一种特殊情况
菱形继承有数据冗余和二义性的问题。class Person
{
public:string _name;int _a[1000];
};class Student :public Person
{
public:int _num;
};class Teacher :public Person
{
public:int _id;
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse;
};int main()
{//二义性、数据冗余Assistant a;a._id = 1;a._num = 2;a.Student::_name = "小张";a.Teacher::_name = "张老师";cout << sizeof(a) << endl;return 0;
}
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。 //虚继承解决数据冗余和二义性
class Person
{
public:string _name;int _a[1000];
};class Student : virtual public Person
{
public:int _num;
};class Teacher : virtual public Person
{
public:int _id;
};class Assistant : public Student, public Teacher
{
protected:string _majorCourse;
};int main()
{//二义性、数据冗余Assistant a;a._id = 1;a._num = 2;a.Student::_name = "小张";a.Teacher::_name = "张老师";cout << sizeof(a) << endl;return 0;
}
以下代码为例
class A
{
public:int _a;
};class B : virtual public A
{
public:int _b;
};class C :virtual public A
{
public:int _c;
};class D : public B, public C
{
public:int _d;
};int main()
{D d;d.B::_a = 1;d.C::_a = 2;d._b = 3;d._c = 4;d._d = 5;d._a = 0;return 0;
}
A一般叫虚基类,在D里面,A放到一个公共位置,那么BC有时需要找A,通过B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。