继承派生

继承

学习继承,掌握如何在原来的类的基础快速增加新的功能,而不影响原来的类,也不改变原来类的代码,方便对于功能的扩展

类和类之间的关系:包含,友元,继承

一、继承和派生

1、继承的概念

1.继承是一种创建新类的方式,新建的类可以继承一个或者多个类。可以理解为子承父业

2.所以继承描述的是类和类之间的关系

3.新建的类被称之为派生类(子类),之前就存在的类被称之为基类(父类)

2、继承和派生

1.继承和派生是同一个过程,从不同的角度来看的

2.一个新类从已经存在的类那里获取其原来已有的特性,这个就叫继承(儿子继承父亲的财产)

3.从已存在的类产生一个子类,这个就叫派生(父亲吧财产给儿子)

3、继承分类

1.新建的类继承一个类,这个就是单继承

2.新建的类继承多个类,这个就叫多继承

二、继承方式

1、如何用一个类去继承另一个类

class A{};
class B:public A{};//单继承
class C{};
class D{};
class E:public C,public D{};//多继承

2.示例

#include<iostream>
using namespace std;
//三种继承方式:就是三种访问属性对应
class father//基类,父类
{
public:
	int id;
	void fun()
	{
		cout << id << endl;
	}
protected:
	int b;
private://基类的私有属性都不能访问,只能继承
	int c;
};
class son :public father
{
public:
	void fun1()
	{
		id = 1;
		b = 2;
		//c = 3;基类的私有成员可以继承,但是不能访问
	}

};
//派生类继承基类之后,派生类拥有父类所有的成员(构造析构除外)
//保护属性和私有属性的区别就是保护属性的成员继承之后,可以在类中访问,私有属性的成员不行,类外二者都不能
//根据继承方式和基类的访问属性,来决定基类的成员在派生类中的访问属性
//看继承方式和基类的访问属性,谁更严格就按照更严格的来访问
//public<protected<private
//基类的私有成员可以继承,但是不能直接访问
//继承方式,建议使用public继承,这样会最大限度的保留访问属性
void publicfun()
{
	son p1;
	p1.id;
	//公有继承,在类外通过子类对象只能访问公有属性下面的成员
}
class son1:protected father//类外都不能访问了,需要公有属性的接口
{
public:
	void fun()
	{
		id = 1;
		b = 2;
		//c = 3;私有的还是不能访问
	}
};
class son2 :private father
{
public:
	void fun()
	{
		id = 4;
		b = 3;
	}
};
int main()
{
	son s;
	s.id = 20;
	son* p = &s;
	s.fun();
	
	system("pause");
	return 0;
}

3、继承和派生的特点

  1. 派生类继承基类之后,派生类拥有父类所有的成员(构造析构除外)
  2. 保护属性和私有属性的区别就是保护属性的成员继承之后,可以在类中访问,私有属性的成员不行,类外二者都不能被访问
  3. 根据继承方式和基类的访问属性,来决定基类的成员在派生类中的访问属性
  4. 看继承方式和基类的访问属性,谁更严格就按照更严格的来访问
    public<protected<private
  5. 基类的私有成员可以继承,但是不能直接访问
  6. 继承方式,建议使用public继承,这样会最大限度的保留访问属性
  7. 公有继承,在类外通过子类对象只能访问公有属性下面的成员

三、继承之后

1、派生类的构成

1.派生类会继承除基类的构造析构函数之外的所有变量成员和函数成员,但是也会使用构造析构函数的

2.可以在派生类中添加新成员,通过派生类对象来调用

3.如果派生类中添加的成员名和基类成员名相同,那么派生类会隐藏基类的成员,可以通过<基类名::基类成员名>来访问,如果是继承的多个基类,而多个基类也有同名的,也是通过这种方法调用同名的成员。

也可以在调用的时候,通过作用域运算符( ::)说明一下这个成员是谁的

2、派生类和基类的关系

1.派生类是基类对象,但是基类对象不是派生类对象

也就是说,派生类的对象可以当基类的对象来使用,因为派生类继承了基类的所有成员

//派生类 派生对象名;
//基类 基类对象名;
//基类对象名=派生对象名;允许
//派生对象名=基类对象名;错误

派生类继承了基类的所有成员,所以派生对象可以给基类赋值,因为基类中有的数据,派生类也有,但是反过来不行,因为派生类中可以添加新的数据

3、派生类的构造析构顺序

1.派生类对象在实例化的时候是会调用基类的构造函数的,先基类后派生类(先有父亲后有儿子),释放就是先父亲后儿子,因为在栈区,先进后出

2.如果是多继承与单继承中构造顺序一致,区别在于,在构造基类时有多个基类,那么会按照基类在初始化列表的声明顺序来依次调用基类的构造函数

3.所以在写继承的时候,要确保基类有可以调用的构造函数,如果是带参构造,就需要使用初始化列表了。初始化列表写在构造函数后面,可以传形参,也可以传实参

class A
{
    public:
}
class B:public A
{
    public:
}
//如果这样实例化B类对象,那么会调用A的默认构造,A有自己写的构造函数,那就调用自己写的

class A
{
    public:
    A(int a)
    {
        
    }
}
class B:public A
{
    public:
}
//这样实例化B类对象,那么就会报错,会提示A类没有合适的构造函数调用,所以需要初始化列表调用父类的单参构造
//带参构造
B():A(1)
{
    
}

4.如果类中有对象成员,默认调用的是成员的无参构造(默认构造),如果没有这个构造函数,那么就要出错,调用可用的构造函数

四、菱形继承

1、菱形继承:有类A,类B,C分别继承A,类D继承类B,C。

这样的继承会导致类D继承了两份类A的成员,在类D对象,想要访问类A的成员的时候,会导致访问不明确,因为类B,C各自继承类A的成员

2、解决方法

1.通过类名::成员来调用那个成员

2.通过虚继承:

在继承方式的前面加上关键字virtual,虚继承之后会使在虚继承的类中多个指针,但是在最后的D类不会在继承两份A的成员,那么就不会有访问的问题

正常:

class A //4字节
{
	int a = 1;
};
class B :public A//8字节
{
	int c = 2;
};
class C :public A//8字节
{
	int d = 3;
};
class D :public B, public C//20字节,两份A的内存
{
	int e = 4;
};

虚继承之后:

class A
{
	int a = 1;
};
class B :virtual public A//多了一个指针,内存+4,一共为12字节
{
	int c = 2;
};
class C :virtual public A//多了一个指针,内存+4,一共为12字节
{
	int d = 3;
};
class D :public B, public C//内存为,两个B,C的指针地址(8字节),3个int型(12字节)[只有1分A的内存了],1个自身的int型(4字节),一共24字节
{
	int e = 4;
};
posted @ 2021-02-22 22:38  kisfly  阅读(68)  评论(0编辑  收藏  举报