目录
C++通过类派生的机制来支持继承。被继承的类称为基类或超类,新产生的类称为派生类或者子类。基类和派生类的集合被称作类继承层次结构。
由基类派生出,派生类的设计形式为:
- class 派生类名: 访问限定符 基类名
- {
- private:
- 成员表1;//派生类增加或替代的私有成员
- public:
- 成员表2;//派生类增加或替代的公有成员
- protected:
- 成员表3;//派生类增加或替代的保护成员
-
- };
注意:派生反映了事物之间的联系,事物的共性与个性之间的关系。派生与独立设计若干相关的类,前者工作量少,重复的部分可从基类继承,不需要单独编程。继承是类型设计层面的复用。
有三种类型的访问限定符:
私有(private)成员:私有成员变量或者函数在类的外部是不可访问的,只有本类中或者友元函数可以访问。定义一个类时,如果不显示地给成员加访问修饰符,类的所有成员都是私有的。
保护(protected)成员:保护成员变量或者函数和私有成员十分相似,不同的是保护成员在其派生类(子类)中是可以访问的。
也就是说私有private比protected的保护性更强。
类中成员变量可以使用这三种访问限定符修饰,继承方式也有这三种方式,我们逐一来讨论:基类中不同的访问限定符的成员以不同的继承方式继承后在派生类中的访问限定为:
示例:
- #include
-
- using namespace std;
-
- class A {
- public:
- int m_i;
- protected:
- int m_j;
- private:
- int m_k;
- static int m_m;
- };
- //public继承在本类的成员函数中,可以访问基类的公有和保护
- //外部方法可以访问本类公有,不能访问保护和私有
- class B :public A
- {
- public:
- void fun()
- {
- m_i = 10;
- m_j = 20;
- //m_k = 30;//error 能继承,不能访问
- }
- };
- //protected继承
- class C :protected A
- {
- public:
- void test()
- {
- m_i = 10;
- m_j = 20;
- //m_k = 30;//error 能继承,不能访问
- }
- };
- class D :private A
- {
- public:
- void test1()
- {
- m_i = 10;
- m_j = 20;
- //m_k = 30;//error 能继承,不能访问
- }
- };
- int main()
- {
- B b;
- b.fun();
- b.m_i = 10;
- // b.m_j = 20; 外部不能访问类中保护成员
- //b.m_k = 30;不能私有成员访问
-
- C c;
- c.test();
- //都不能在外部访问,保护继承
- // 就把A公有的继承为了保护,保护的继承为了保护,私有还是私有
- //c.m_i = 10;
- //c.m_j = 20;
- //c.m_k = 30;
-
- D d;
- //都不能在外部访问,私有继承
- // 把A里面成员变量函数都变成私有的
- //D.test1();
- //d.m_i = 10;
- //d.m_j = 20;
- //d.m_k = 30;
- }
【1. 以public方式继承】
【2. 以protected方式继承】
【3. 以private方式继承】
对于这三种方式继承的 派生类 来说: 都能访问基类的public, protected 成员;
public 的方式继承到派生类,这些成员的权限和在基类里的权限保持一致;
protected方式继承到派生类,成员的权限都变为protected;
private 方式继承到派生类,成员的权限都变为private;
继承方式\基类成员访问呢权限 | public | protected | private |
public | public | protected | 不可访问 |
protected | protected | protected | 不可访问 |
private | private | private | 不可访问 |
区别在于多继承之后的访问权限
给上述代码例子多继承一次就会发现:
注意:
构造函数和析构函数不能被继承。基类的构造和析构满足的工作要求不可能完全适应派生类,因此需要重写构造,析构。
其他函数都可以通过类对象来调用,创建对象时由系统调用该对象所属类的构造函数,该对象生存期也只调用这一次。由继承而来的派生类对象,是能够调用父类函数,但不能调用构造函数以及析构函数。
在派生类中,基类的内存布局优先于派生类。
- class Object
- {
- private: int oa;
- protected: int ob;
- public: int oc;
-
- };
- class Base:public Object
- {
- private: int bx;
- protected: int by;
- public: int bz;
- };
- int main()
- {
- Object obj;
- Base base;
- return 0;
- }
- class A
- {
- public:A(int i = 0) { cout << "A" << i << endl; }
- };
- class B
- {
- public:B(int i = 0) { cout << "B" << i << endl; }
- };
- class C
- {
- public:C(int i = 0) { cout << "C" << i << endl; }
- };
- class D :public B, public A
- {
- public:
- D(int i = 0, int j = 0, int k = 0) :A(i), B(j), c(k), m_m(10)
- {
- cout << "D" << m_m << endl;
- }
- private:
- C c;
- int m_m;
- };
- void main()
- {
- D d(1, 2, 3);
- }
调用顺序:
1.按照继承顺序调用构造
2.按照组合顺序调用构造
3.调用本类自己的构造
- class Object
- {
- private:
- int value;
- public:
- Object(int x = 0):value(x){}
- ~Object(){}
- void Print(int x)
- {
- value = x;
- cout << value << endl;
- }
-
- };
- class Base :public Object
- {
- private:
- int num;
-
- public:
- Base(int x=0):Object(x),num(x+10){}
- };
- int main()
- {
- Object obja(0);
- Base base(10);
- Object* op = &base;
- Object& ob = base;
- obja = base;
- // base = obja;//error 父类对象不能给子对象赋值
- return 0;
- }
几个例题:
- //不能被继承的类
- //final 表示当前类终止了
- class A final
- {
- private:
- A() {}
- };
- class B :public A
- {
-
- };
- int main()
- {
- //B b;
- }
-
-
- //能被继承,不能在外界定义对象的类
- class A
- {
- protected:
- A() { cout << "A" << endl; }
- };
- class B :public A
- {
- public:
- B() { cout << "B" << endl; }
- };
- void main()
- {
- //A a; //外部不能定义a类对象
- B b;
- }
-
-
- //设计一个类,只能生成一个实例
- class A
- {
- private:
- A() { cout << "A" << endl; }
- public:
- A(const A& a) = delete;
- A& operator=(const A& a) = delete;
- static A&& GetA()
- {
- cout << "Get(a)" << endl;
- return A();
- }
- void Print() { cout << "Print" << endl; }
- };
- void main()
- {
- //A a, b, c, d;
- A::GetA().Print();
- //A a = A::GetA();
- }
实现子类的拷贝,析构,赋值,编写继承关系
- //如果有指针了作为数据成员 析构,深拷贝,深赋值必须要写
- class Person {
- public:
- Person(){}
- Person(const char* name, char sex, int age) :m_sex(sex), m_age(age)
- {
- cout << "Person name sex age" << endl;
- m_name = new char[strlen(name) + 1];
- strcpy(m_name, name);
-
- }
- Person(const Person& p)
- {
- m_name = new char[strlen(p.m_name) + 1];
- strcpy(m_name, p.m_name);
- m_age = p.m_age;
- m_sex = p.m_sex;
- }
- ~Person()
- {
- cout << "Person析构" << endl;
- if (m_name == nullptr)
- {
- delete[]m_name;
- m_name = nullptr;
- }
- }
- Person& operator = (const Person& p)
- {
- if (this != &p)
- {
- delete[] m_name;
- m_name = new char[strlen(p.m_name) + 1];
- strcpy(m_name, p.m_name);
- m_sex = p.m_sex;
- m_age = p.m_age;
- }
- return *this;
- }
- void Show()
- {
- cout << "name : " << m_name << endl;
- cout << "sex : " << m_sex << endl;
- cout << "age : " << m_age << endl;
- }
- private:
- char* m_name;
- char m_sex;
- int m_age;
- };
- class Student : public Person
- {
- public:
- Student(){}
- Student( const char*name, const char sex, int age, const char* num, float sorce): Person(name, sex, age)
- {
- m_num = new char[strlen(num)+1];
- strcpy_s(m_num,strlen(num)+1, num);
- m_sorce = sorce;
- cout << "Student" << endl;
- }
- Student(const Student& s) :Person(s)
- {
- m_num = new char[strlen(s.m_num) + 1];
- strcpy(m_num, s.m_num);
- m_sorce = s.m_sorce;
- }
- ~Student()
- {
- cout << "Student析构" << endl;
- if (m_num == nullptr)
- {
- delete[]m_num;
- m_num = nullptr;
- }
- }
- Student& operator=(const Student& s)
- {
- if (this != &s)
- {
- Person::operator = (s);//指定作用域调用父类的赋值运算符重载
- delete[] m_num;
- m_num = new char[strlen(s.m_num) + 1];
- strcpy(m_num, s.m_num);
- m_sorce = s.m_sorce;
- }
- return *this;
- }
- void Print()
- {
- Show();
- cout << "num : " << m_num << endl;
- cout << "score :" << m_sorce << endl;
- }
- private:
- char* m_num;
- float m_sorce;
- };
- int main()
- {
- //Person p;
- Student s("张三", 'm', 20, "1001", 80);
- s.Print();
- cout << "SS info :" << endl;
- Student ss = s; //用s对象去构造ss对象,调用拷贝构造
- ss.Print();
-
- cout << "dd info : " << endl;
- Student dd;
- dd = s; //用s去给dd赋值,需要调用赋值运算符重载
- dd.Print();
- cout << "main end " << endl;
- return 0;
- }
继承与静态成员,静态成员只有一个,所有对象共享(包括基类对象和派生类对象)
- class Shape
- {
- public:
- Shape() {}
- void Print() { cout << "num = " << m_num << endl; }
- //private:
- static int m_num;
- };
- int Shape::m_num = 0;
- class Circle :public Shape
- {
- public:
- Circle() { cout << "C num = " << ++m_num << endl; }
- };
- class Rectangle : public Shape
- {
- public:
- Rectangle() { cout << "R num = " << ++m_num << endl; }
- };
- class Triangle : public Shape
- {
- public:
- Triangle() { cout << "T num = " << ++m_num << endl; }
- };
- void main()
- {
- Shape s;
- Circle c1, c2;
- s.Print();
- Rectangle r1, r2;
- s.Print();
- Triangle t1, t2;
-
- s.Print();
- }
6、1隐藏和重载
示例一:
子类中定义了和基类同名的static属性
如果子类中定义了和基类同名的static属性,则有两份静态区域,各自维护,基类的被隐藏
如果在子类中想访问基类的,则需要显示访问
class A { public: A() { cout << "A:" << ++m_i << endl; } void Print() { cout << "A : m_i:" << m_i << endl; } protected: static int m_i; }; int A::m_i = 10; class B :public A { public: B() { A::m_i++; cout << "B : " << ++m_i << endl; } void Show() { cout << "B : m_i:" << m_i << endl; } private: static int m_i; }; int B::m_i = 20; void main() { A a; a.Print(); //11 B b; a.Print(); //13 b.Show(); //21 }
示例二:
class A { public: A(int i = 0) :m_i(i) {} virtual void fn() { cout << "A : fn" << endl; } private: int m_i; }; class B :public A { public: B(int i = 0, int j = 0) :m_j(j), A(i) {} //定义了一个和基类同名的函数,则将基类的fn函数隐藏 //1隐藏,如果是非virtual,则同名同参或者不同参 void fn() { A::fn(); cout << "B:fn" << endl; } //virtual void fn(); //隐藏2 如果是virtual,则一定不能同参 /*void fn(int n) { A::fn(); cout << "B : fn(int)" << endl; }*/ private: int m_j; }; void main() { // cout << sizeof(B) << endl; B b; //b.fn(10); b.fn(); //从A继承过来的fn函数被隐藏了 }
示例三:
class A { public: A(int i = 0) :m_i(i) {} virtual void fn() { cout << "A : fn" << endl; } private: int m_i; }; class B :public A { public: B(int i = 0, int j = 0) :m_j(j), A(i) {} //定义了一个和基类同名的函数,则将基类的fn函数隐藏 //1隐藏,如果是非virtual,则同名同参或者不同参 /*void fn() { A::fn(); cout << "B:fn" << endl; }*/ //virtual void fn(); //隐藏2 如果是virtual,则一定不能同参 void fn(int n) { A::fn(); cout << "B : fn(int)" << endl; } private: int m_j; }; void main() { // cout << sizeof(B) << endl; B b; b.fn(10); //b.fn(); //从A继承过来的fn函数被隐藏了 }
总结:
重载:
- 1.同一个类
- 2.同名函数
- 3.参数列表不同
- 4.和返回值无关,但是和const有关
- void fn(){}
- void fn()const{}
隐藏:
- 1.父子关系两个类
- 2.如果是非virtual,则同名同参或者不同参都可以
- 3.如果是virtual,则一定不能同参,否则会被隐藏
友元不具有继承性
菱形问题:
一个派生类有两个或以上的基类,这些基类中存在相同的基类即(B继承A ;C继承A ;D继承B和C) 当派生类想要直接调用A类中的方法时,产生二义性,出错。
这里的二义性是由于他们间接都有相同的基类导致的,除了带来二义性外,还会浪费空间(每个派生类中都会带有一份基类的内存)
而为了解决菱形继承问题:让A变为一份,B,C,D可以共享,那么可以将其放在D作用域的md的下面
这就是虚继承,可以用来解决菱形继承出现重复间接基类的问题
虚继承的实现:
class 派生类名:virtual 访问限定符 基类类名{...}
class 派生类名:访问限定符 virtual 基类类名{...}
virtual 关键字只对紧随其后的基类名起作用
- class B :virtual public A //A称为虚基类
-
- class C :public virtual A //A称为虚基类
虚继承添加位置:
可能出现数据重复的直接继承关系,如A重复出现,那么B,C虚继承A。
虚继承的内存布局
示例:从下面沙发类和床品类中抽象出共性,写出一个新类,家具类
- class Furniture//家具
- {
- public:
- Furniture() { cout << "1" << endl; }
- void Sit() { cout << "sit" << endl; }
- private:
- int m_size;
- };
- // vptr(虚指针,占内存,同指针),m_size ---->8
- class Sofa : virtual public Furniture
- {
- public:
- Sofa() { cout << "2" << endl; }
- };
- //vptr,m_size ---->8
- class Bed : virtual public Furniture
- {
- public:
- Bed() { cout << "3" << endl; }
- };
- //vptr(Sofa),vptr(Bed),m_size ---->12
- class SofaBed :public Sofa, public Bed
- {
- public:
- SofaBed() { cout << "4" << endl; }
- };
- int main()
- {
-
- SofaBed sb;
- sb.Sit();
- return 0;
- // cout << sizeof(sb) << endl;
- // cout << sizeof(Sofa) << endl;
- // cout << sizeof(Bed) << endl;
- // sb.Sofa::Sit();
- // sb.Bed::Sit();
- }
- 在没有加virtual之前,重复构造家具 ,结果为 sofa(1 2) -> bed( 1 3) ->4
- 结果1 2 1 3 4
- 加了virtual后 构造家具(属性构造了一份),构造沙发,构造床的时候发现床也是从家具继承过来
- 此时就不再构造家具,接着构造床,最后构造沙发床,保证了家具的属性只有一份拷贝
- 结果 1 2 3 4
日常打卡巩固,知识分享