c++类继承

Posted 东条希尔薇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++类继承相关的知识,希望对你有一定的参考价值。

这里写目录标题

c++类继承

继承简介

概念

继承是一种简化代码的手段之一,使代码可以实现复用。它允许在原来类的功能上进行扩展,衍生出新的功能。也可以把多个类的基本信息提取到某个类,减少代码的冗余。其中被继承的类通常叫做父类或基类,继承下来的类叫做子类或派生类

使用格式

子类名:继承类型 继承的父类。例如:以人类和学生类做例子

class person

public:
    int _name;
    int _age;
;

class stu:public person

    int_ id;

其中,stu就是派生类,而person是父类

继承类型

其中继承类型有三种:public,protect,private继承。其中:

  • public继承下来的父类成员在子类中保持其访问权限
  • private继承下来的父类对象在子类中也变为private成员
  • 父类的private成员无论怎么样对子类也不可见
  • 但如果想让仅有父子能访问,外部不能访问。可以把父类中的成员属性设置为protected

就像什么呢?爸爸和儿子住一间房子下,虽然家里的东西大家可以共用,但是爸爸总有隐私不能让儿子发现,比如爸爸的私房钱

总结:基类其它成员在子类中的访问方式=min(基类访问限定符,访问方式),权限大小:public>protected>private。但实际中基本用public继承

class Father

public:
	int _a;
protected:
	int _b;
private:
	int _c;
;

class Son :public Father

public:
	int _d;
;

上述代码中,外部只能访问a和d,子类还可以访问b,但c只有父类能访问

基类和派生类的类型转换及切片

首先声明:切片不是强制类型转换,而是语法特性的一种

派生类可以赋值给父类,但父类不能赋值给子类

Father f1;
Son s1;
f1=s1;

为什么能这么赋值呢?

我们知道,子类中的成员肯定是不少于父类的成员的。在赋值时,子类只需要把父亲没有的成员给丢弃后赋值给子类,就像切面包一样一刀切下去,所以也叫做切片

那有没有基类赋值给派生类的方式呢?有的,用指针就行了

Son s1;
Father* f1=&s1;

但通过指针父类只能访问它有的成员,访问子类成员时程序就会崩溃

使用注意:只建议在public继承下使用

基类和子类的作用域及隐藏

作用域

我们知道,程序以一对花括号来表示不同的作用域,例如:

int a=0;

    int a=1;
    cout<<a<<endl;//1

cout<<a<<endl;//2

1处的a输出1,2处的a输出0.因为1的作用域在a=1内部,而2的作用域在外部

成员变量隐藏

父类和子类它们属于不同的作用域,在有同名变量被声明时,使用起来也会发生上面代码类型的情况

class Father

public:
    int _a;
;

class Son:public Father

public:
    int _a;
;

Father f1;
Son s1;
f1._a=1;
s1._a=2;
cout<<s1._a<<endl;

在上面的情况,父类的成员将会被屏蔽,子类将会直接访问属于它的成员,输出为2

虽然语法上支持这种写法,但是,这样写代码非常容易造成二义性混乱,所以写代码要规避这种情况的发生

函数隐藏

条件:只要父类和子类有相同名字的函数就构成隐藏(忽略参数和返回值)

class Father

public:
    void func()
    
        cout<<"Father"<<endl;
    
;

class Son:public Father

public:
    void func(int i)
    
        cout<<"Son"<<endl;
    
;

Father f1;
Son s1;
s1.func();//这么写会报错,因为隐藏了父类的func函数,所以提示缺少参数
s1.func(10);//正常调用

派生类的默认成员函数

我们知道,在我们写一个类的时候,编译器通常会给我们形成以下的默认成员函数:

  • 构造
  • 析构
  • 拷贝
  • 赋值

那么,由于派生类其中有父类的存在。派生类的这些函数到底是继承父类的呢还是自己实现的呢?

c++中采用了比较简单的方式,就是各管各的

总的来说,在派生类生成的默认函数中,父类的成员部分由父类的函数自己完成,而派生类独有的成员需要在派生类中自己实现。

  • 如果派生类中没有显示的定义默认函数,那么生成的函数中,父类部分调用父类的成员函数,子类的按普通类处理

    普通类没有定义默认函数的处理方式:内置类型不处理(随机值),自定义类型调用它的默认函数

    class Father
    
    public:
    	Father()
    		:_a(1)
    	
    
    	
    	int _a;
    ;
    
    class Son :public Father
    
    public:
    	int _b;
    ;
    
    cout<<Son()._a<<" "<<Son()._b<<endl;
    

    在上述代码中,a输出1,因为派生类中默认调用的父类的构造。b输出随机值,因为派生类中未作处理

  • 如果定义了,会有一个顺序问题,构造函数会先调用父类,而析构函数先调用派生类

如果父类没有默认构造或其它成员函数,那么在派生类中必须显示调用父类的一部分

class Father


public:
	Father(int i)
		:_a(i)
	

	
	int _a;
;

class Son :public Father

public:
    Son()
	

	
	int _b;
;

这样的话会直接编译报错,在子类中显示调用就不会

而且在显示调用时,只能在初始化列表中调用

class Son :public Father

public:
	Son()
		:Father(1)
	
		
	
	int _b;
;

多继承及其带来的菱形继承问题

多继承

c++中允许一个派生类继承自多个基类,连接继承关系时使用逗号

class A

public:
	int _a;
;

class B

public:
	int _b;
;

class C :public A, public B

public:
	int _c;
;

c可以访问到a和b两个成员变量

但是多继承是c++的一个早期设计的大坑,它有可能会带来数据冗余和二义性的问题

看下面的代码

class A

public:
	int _a;
;

class B:public A

public:
	int _b;
;

class C :public A

public:
	int _c;
;

class D:public B,public C

public:
	int _d;
;

它们的继承关系是这样的

我们可以发现,在D这个类中,_a有两份!

因为D分别继承B和C,而BC都继承了A,所以D自然继承了BC中都包含的A的那一部分,造成了数据的冗余

而在使用的时候,不能直接这样

D d;
d._a=1;

会提示数据不明确而报错

解决方式:在_a前加上限定符

d.B::_a=1;

这个就是菱形继承了

但这么写相当的奇怪,我们必须想办法解决

菱形继承解决方法及其原理

我们先观察一下D类的内存情况吧

D d1;
d1.B::_a = 1;
d1.C::_a = 5;
d1._b = 2;
d1._c = 3;
d1._d = 4;

其中谁先继承,谁就在内存的偏前位置

我们看到,圈出来的1和5都是属于A类的,确实冗余了

为了更正这个问题,后面提出了虚继承的概念

虚继承的使用,就是在可能会造成二义性的类前加入virtual修饰,(下面的B和C,因为同时继承了A,就可能造成二义性)这样就解决了数据二义性问题

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;
;

cout<<D._a<<endl;//这样使用就不会出现不明确的问题了

那么,虚继承的原理是什么呢?同样先观察内存

我们发现,我们能找到我们定义的变量,但中间的一些奇怪的数字是啥呢?

看着结构有点像地址,我们再跟踪地址找找看吧

我们发现它指向了一个值一个14一个0c,因为这是16进制,我们把它转换为10进制

然后在加上B类和C类中的地址,我们发现它居然指向一个值,就是本章第一张图片下面的01

那么,结论就来了

虚继承中会在每个派生类类中加入一张虚基表。先把父类的成员放在一个公共区域,虚基表中存了公共区域相对于此类的偏移量,那么每个类就可以计算出父类成员的位置了

以上是关于c++类继承的主要内容,如果未能解决你的问题,请参考以下文章

C++ 虚继承的对象模型

C++ 设计模式:可能有继承的方法私有到派生类?

c++面向对象的程序设计

JAVA多继承例子

JAVA多继承例子

C++ 赋值运算符关于继承的问题