三、C++语言初阶:继承

3:继承

3.1 语法:

父类/子类
基类/派生类

class 派生类 : [访问限定符] 基类 {
  成员
}

注:如果不写继承访问限定符,默认是private

3.2 成员访问权限

publicprotectedprivate
类成员函数
友元函数
子类函数×
类对象××

1、子类继承了父类所有的成元变量和成员函数。与访问限定符无关。访问限定符只是限制了访问。
2、子类访问父类成员变量,把父类成员变量访问限制符,改为protected

  • 实例:
#include <iostream>
using namespace std;
class Base{
public:
    void public_func(){
    	cout << __func__ <<endl;
    }
    //类内访问
    void Test(){
        public_func();
		protected_func();
		private_func();
    }
protected:
    void protected_func(){
    	cout << __func__ << endl;
    }
private:
    void private_func(){
    	cout << __func__ << endl;
    }
};
class Derive:public Base{
public:
     //对于public继承,子类内部可以访问父类public和protected成员
     void Func(){
     	public_func();
		protected_func();
 		//private_func();
     }
};
int main(){
    Base b;
    b.Test();

    //对象访问
    b.public_func();
    //b.protected_func();
    //b.private_func();

    cout << "Derive:" << endl;
    Derive d;
    d.Func();
    d.public_func();
    //d.protected_func();
    //d.private_func();
}
public_func
protected_func
private_func
public_func
Derive:
public_func
protected_func
public_func

继承访问权限变化
分为子类内部子类对象两种访问方式。

1、子类内部访问:
子类内部访问public继承的父类成员变量
子类内部访问public继承的父类成员函数
2、子类对象访问:
子类对象访问public继承的父类成员变量
子类对象访问public继承的父类成员函数
实例:

#include <iostream>
using namespace std;
class Base {
public:
    int public_data = 1;
    void public_func(){};
protected:
    int protected_data = 2;
    void protected_func(){};
private:
    int private_data = 3;
    void private_func(){};
};
class Derive:public Base {
public:
    //子类内部访问public继承的父类成员函数
    void test() {
        public_func();
        protected_func();
        //private_func();  //无法访问
    }
    //子类内部访问public继承的父类成员变量
    void test2() {
        cout<< public_data << endl;
        cout<< protected_data << endl;
        //cout<< private_data <<endl; //无法访问
    }
};
int main(){
    Derive d;
    d.test();
    d.test2();
    //子类对象访问
    //只有public继承时,子类对象才能访问父类的public成员函数和成员变量,其他均不能访问
    d.public_func();
    //d.protected_func(); 
    //d.private_func();
    cout << d.public_data << endl;
    //cout << d.protected_data << endl; //无法访问
    //cout << d.private_data << endl; //无法访问
}
1
2
1

结论
1、子类内部访问父类成员(成员函数和成员变量)

publicprotectedprivate
public 继承×
protected 继承×
private 继承×

2、子类对象访问父类成员(成员函数和成员变量)

publicprotectedprivate
public继承××
protected 继承×××
private 继承×××

结论:子类对象只有public继承父类的时候,才能访问父类的public成员,其他都不能访问。
注:通常子类使用public继承父类。

子类对象访问父类成员访问限定符的变化

继承方式\父类成员publicprotectedprivate
public 继承publicprotected不可见
protected 继承protectedprotected不可见
private 继承privateprivate不可见

public继承
在这里插入图片描述
protected继承
在这里插入图片描述
private继承
在这里插入图片描述
小结:
public无论类内部还是类对象都可以访问。
protected类对象不可访问,类内部与继承类的内部可以访问。
private只有类内部可以访问。

3.3 继承关系的构造顺序

  • 派生类的构造函数与析构函数的调用顺序
    1、派生类的构造函数调用顺序
    父对象构造、成员变量构造、子对象构造的顺序
    2、派生类的析构函数调用顺序
    子对象析构、成员变量析构、父对象析构的顺序

实例:父类、子类和子类成员的构造顺序

#include <iostream>
using namespace std;
class Member{
public:
    Member(){
        cout << __func__ << endl;
    }
    ~Member(){
        cout << __func__ << endl;
    }
};
class Parent{
public:
    Parent(){
        cout << __func__ << endl;
    }
    ~Parent(){
        cout << __func__ << endl;
    }
};
class Son:public Parent{
Member m;
public:
    Son(){
        cout << __func__ << endl;
    }
    ~Son(){
        cout << __func__ << endl;
    }
};
int main(){
    Son s;
}
Parent
Member
Son
~Son
~Member
~Parent

实例2:类成员变量和类的构造顺序

#include <iostream>
using namespace std;
//组合
class Member{
public:
    Member(){
    	cout << __func__ << endl;
    };
    ~Member(){
    	cout << __func__ << endl;
    }
};
class Member2{
public:
    Member2(){
    	cout << __func__ << endl;
    };
    ~Member2(){
    	cout << __func__ << endl;
    }
};
class Member3{
public:
    Member3(){
    	cout << __func__ << endl;
    };
    ~Member3(){
    	cout << __func__ << endl;
    }
};
class Simple{ 
// 成员的定义顺序
Member m;
Member2 m2;
Member3 m3;
public:
    // Simple(){
    //Simple():m(),m2(),m3(){
    Simple():m3(),m2(),m(){ // 初始化列表不决定初始化顺序,初始化顺序与成员定义顺序有关
    	cout << __func__ << endl;
    };
    ~Simple(){
    	cout << __func__ << endl;
    }
};
int main(){
    Simple d;
}
Member
Member2
Member3
Simple
~Simple
~Member3
~Member2
~Member

3.4 同名隐藏

概念:子类的成员函数基类成员函数同名,子类的函数将会隐藏基类的所有同名函数。

实例:同名隐藏

#include <iostream>
using namespace std;
class Base{
public:
    Base(){
    	cout << __func__ << endl;
    }
    ~Base(){
    	cout << __func__ << endl;
    }
    void Test(){
    	cout << "Base::Test" << endl;
    }
    void Test(int n){
    	cout << "Base::Test(" << n << ")" << endl; 
    }
};

class Derive:public Base{
public:
    Derive(){
    	cout << __func__ << endl;
    }
    ~Derive(){
    	cout << __func__ << endl;
    }
    void Test(){
    	cout << "Derive::Test" << endl;
    }
    void Test(int n){
    	cout << "Derive::Test(" << n << ")" << endl; 
    }
};

int main(){
    Derive d;
    d.Test();
    d.Test(100);
}
Base
Derive
Derive::Test
Derive::Test(100)
~Derive
~Base

解决方法:
1、在Derive类对象调用被隐藏的父类函数时,在函数前面加上父类限定符。例如:

d.Base::Test(); // 解决同名隐藏第一种方法

2、Derive类中的名称会隐藏Base类中同名的名称,在public继承中我们可以通过引入using声明。

class Derive:public Base{
public:
    using Base::Test;
    Derive(){ //Derive():Base(){}
    	cout << __func__ << endl;
    };
   	~Derive(){
    	cout << __func__ << endl;
    }
    void Test(){ // 同名隐藏(只要父类相同名字的成员都被隐藏)
    	cout << "Derive::Test" << endl;
    }
};

实例:

#include <iostream>
using namespace std;
class Base{
public:
    Base(){
    	cout << __func__ << endl;
    };
    ~Base(){
    	cout << __func__ << endl;
    }
    void Test(){
    	cout << "Base::Test" << endl;
    }
    void Test(int n){
    	cout << "Base::Test(" << n << ")" << endl;
    }
};
class Derive:public Base{
public:
    using Base::Test; 
    Derive(){ //Derive():Base(){}
    	cout << __func__ << endl;
    };
    ~Derive(){
    	cout << __func__ << endl;
    }
    void Test(){ // 同名隐藏(只要父类相同名字的成员都被隐藏)
    	cout << "Derive::Test" << endl;
    }
};
int main(){
    Derive d;
    d.Base::Test(); // 解决同名隐藏第一种方法
    // d.Base::Test(10);
    d.Test(10); // 解决同名隐藏的第二种方法
}
Base
Derive
Base::Test
Base::Test(10)
~Derive
~Base

3.5 函数同名的情况

名称英语作用域形参virtual
重载overload相同的范围(在同一个类中)函数名相同,参数列表不同virtual关键字可有可无
重写(覆盖)override不同的范围,分别位于基类和派生类中函数名相同 ,参数列表相同基类函数必须有virtual关键字
隐藏hide不同的范围,分别位于基类和派生类中函数名相同,参数列表可同可不同(1)如果派生类的函数和基类的函数同名,但是参数不同,此时,不管有无virtual,基类的函数被隐藏 (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字,此时,基类的函数被隐藏
  • 赋值兼容规则
    概念:在任何需要基类对象的地方都可以使用公有的派生类对象来代替。反之,不可。

三种情况
1、派生类的对象可以赋值给基类对象

Base base;
Derive derive;
base = derive;

对象切割(Object Slicing):在赋值时舍弃派生类自己的成员,只进行基类数据成员的赋值
2、派生类的对象可以初始化基类的引用

Derive derive;
Base& base = derive;

3、派生类的对象可以赋值给基类的指针
指向基类对象的指针变量可以指向派生类对象。

Derive derive;
Base* base = &derive;

实例:

#include <iostream>
using namespace std;
class Base{
protected:
    int m;
    int n;
public:
    Base(int n,int m):n(n),m(m){
    	cout << __func__ << endl;
    };
    ~Base(){
    	cout << __func__ << endl;
    }
    void Test(){
    	cout << "Base::Test(" << n << "," << m << ")"<< endl;
    }
    void Test(int n){
    	cout << "Base::Test(" << n << ")" << endl;
    }
    void Func(){
    	cout << "Base:Func" << endl;
    }
};
class Derive:public Base{
float f;
public:
    using Base::Test;
    Derive(int n,int m,float f):Base(n,m),f(f){ //Derive():Base(){}
    	cout << __func__ << endl;
    };
    ~Derive(){
    	cout << __func__ << endl;
    }
    void Test(){ // 同名隐藏(只要父类相同名字的成员都被隐藏)
    	cout << "Derive::Test(" << n << "," << m << "," << f << ")"<< endl;
    }
    void Func(){
    	Base::Test();
		//Base::Test(10);
    	Test(10);
    }
};
void Func(Base& b){
    b.Test();
}
int main(){
    Derive d(1,2,3.14);
    d.Test();  //调用子类Test()
    d.Func();
    Func(d); //调用父类Test()
	//派生类的对象可以初始化基类的引用
    Base& b = d;
    b.Test();
    b.Test(10);
    cout << &d << ":" << &b << endl;
    
	//派生类的对象可以赋值给基类的指针
    Base* p = &d;
    p->Test();
    p->Test(20);
    //决定调用哪个函数的是类型
    
    //派生类的对象可以赋值给基类对象
    Base b2 = d;
    b2.Test();
}
Base
Derive
Derive::Test(1,2,3.14)
Base::Test(1,2)
Base::Test(10)
Base::Test(1,2)
Base::Test(1,2)
Base::Test(10)
0x7ffde44fa9140x7ffde44fa914
Base::Test(1,2)
Base::Test(20)
Base::Test(1,2)
~Base
~Derive
~Base

3.6 多重继承

一个类可以同时继承多个父类的行为和特征功能。
逗号分割的基类列表
语法:

class 类名 : public 基类1,public 基类2{
};

3.7 菱形继承/钻石继承

概念:两个子类继承同一个父类,而又有子类同时继承这两个子类。
实例:等腰直角三角形继承等腰三角形与直角三角形

#include <iostream>
#include <cmath>
using namespace std;
//三角形
class Triangle{
protected: //允许子类访问,不允许对象访问
    int a,b,c;
public:
    Triangle():a(0),b(0),c(0){} //初始化成员变量
    Triangle(int a,int b,int c):a(a),b(b),c(c){} //构造函数
    int GetLength() const { 
    	return a+b+c;
    }
    float GetArea(){
    	float q = GetLength()/2.0;
	return sqrt(q*(q-a)*(q-b)*(q-c));
    }
};
//等边三角形
class EqualTriangle:public Triangle{
public:
    /*
    EqualTriangle(int side){
	a = b = c = side;
    }
    */
    EqualTriangle(int a):Triangle(a,a,a){}
};
//等腰三角形
class IsoTriangle:public virtual Triangle{
public:
    //IsoTriangle(int side,int iso):Triangle(side,iso,iso){}
    //如果父类没有默认构造函数,子类初始化必须调用父类构造函数
    //子类的构造函数初始化列表只能初始化自身的成员变量,不能直接初始化父类成员变量
    IsoTriangle(int side,int iso){
    	a = side;
	b = iso;
	c = iso;
    }
    //直接使用父类成员变量
    //继承只代表拥有,不代表可以访问
};
//直角三角形
class RightAngleTriangle:public virtual Triangle{
public:
    RightAngleTriangle(int a,int b,int c):Triangle(a,b,c){}
};
//等腰直角三角形
//等腰三角形与直角三角形形成虚继承关系,四者形成菱形关系
class IsoRightAngleTriangle:public IsoTriangle,public RightAngleTriangle{
public:
    IsoRightAngleTriangle(int side,int iso):IsoTriangle(side,iso),RightAngleTriangle(side,iso,iso){}
};
void Print(Triangle t){
    cout << t.GetLength() << "\t" << t.GetArea() << endl;
}
void Print2(Triangle& t){
    cout << t.GetLength() << "\t" << t.GetArea() << endl;
}
void Print3(Triangle* t){
    cout << t->GetLength() << "\t" << t->GetArea() << endl;
}
int main(){
    int a,b,c;
    cin >> a >> b >> c;
    Triangle t(a,b,c); 
    cout << t.GetLength() << "\t" << t.GetArea() << endl;
    IsoTriangle iso(a,b); 
    cout << iso.GetLength() << "\t" << iso.GetArea() << endl;

    Triangle t2 = iso;
    cout << t2.GetLength() << "\t" << t2.GetArea() << endl;

    Print(t);
    Print(iso); //Triangle t = iso;

    Print2(t);
    Print2(iso); //Triangle& t = iso;

    Print3(&t);
    Print3(&iso); //Triangle* t = &iso;
    //赋值兼容原则,任何使用父类对象的地方,都可以使用public继承的子类对象代替
    Triangle& f = iso;
    Triangle* p = &iso;

    cout << "------------" << endl;
    Print2(f);
    Print3(p);

    cout << "------------" << endl;

    Triangle* arr[] = {
    	new Triangle(2,3,4),
	new IsoTriangle(3,5),
	new EqualTriangle(3),
	new IsoRightAngleTriangle(sqrt(2),1),
    };
    //赋值兼容原则
    for(int i = 0;i < 4;++i){
    	Print3(arr[i]);
    }
}
2 3 4
9	2.90474
8	2.82843
8	2.82843
9	2.90474
8	2.82843
9	2.90474
8	2.82843
9	2.90474
8	2.82843
------------
8	2.82843
8	2.82843
------------
9	2.90474
13	7.15454
9	3.89711
3	0.433013

3.8 关于多重继承

1、什么是多重继承?同时继承多个父类。
2、多重继承有什么危害?菱形继承/钻石继承。
3、什么是菱形继承/钻石继承?多重继承的两个或多个父类具有相同的祖先类。
4、菱形继承/钻石继承有什么危害?因为多重继承的两个或多个父类具有相同的祖先类。所以会有完全相同的属性和方法。因此当前多重继承类有两份相同的属性和方法。使用时会出现冲突。
5、如何解决菱形继承/钻石继承导致的冲突?使用虚继承。
6、什么是虚继承?父类在继承具有相同的祖先类时,加上virtual.

解决钻石继承的危害:虚继承&虚基类
虚继承:在继承定义中包含了virtual关键字的继承关系。
虚基类:在虚继承体系中的通过virtual继承而来的基类。
语法:

class 类名:public virtual 基类{
}

虚基类是一个相对概念,在虚继承关系中,父类相对与子类是虚基类。

3.9 对象构造顺序

在这里插入图片描述

  • 基本原则
    先父后子
    从左到右
    先虚后实
    从上到下
    由内及外

实例:

#include <iostream>

using namespace std;

#define SIMPLE_CLASS(name)\
class name{\
public:\
    name(){ cout << #name << " Constructor" << endl;}\
    ~name(){ cout << #name << " Destructor" << endl;}\
};

SIMPLE_CLASS(Base1)
SIMPLE_CLASS(Base2)
SIMPLE_CLASS(Base3)

SIMPLE_CLASS(VBase1)
SIMPLE_CLASS(VBase2)
SIMPLE_CLASS(VBase3)

SIMPLE_CLASS(Member1)
SIMPLE_CLASS(Member2)
SIMPLE_CLASS(Member3)

#undef SIMPLE_CLASS

class Test : public Base1,
             public Base2,
             public Base3,
             public virtual VBase1,
             public virtual VBase2,
             public virtual VBase3 {
public:
  Test() { cout << "Test Constructor" << endl; }
  ~Test() { cout << "Test Destructor" << endl; }
private:
  Member1 m1;
  Member2 m2;
  Member3 m3;
};

int main() {
  Test t;
}
VBase1 Constructor
VBase2 Constructor
VBase3 Constructor
Base1 Constructor
Base2 Constructor
Base3 Constructor
Member1 Constructor
Member2 Constructor
Member3 Constructor
Test Constructor
Test Destructor
Member3 Destructor
Member2 Destructor
Member1 Destructor
Base3 Destructor
Base2 Destructor
Base1 Destructor
VBase3 Destructor
VBase2 Destructor
VBase1 Destructor
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值