c++中的继承

目录

目录

继承的概念和定义

继承的概念:

继承的定义:

基类和派生类的赋值转换(切片)

继承中的作用域

继承中的默认成员函数

不想让类继承

继承和友元的关系

继承和静态成员的关系

继承中的菱形继承问题

继承里的虚继承

继承和组合


继承的概念和定义

继承的概念:

继承是一种使对象复用的有效手段,被继承的类叫做父类(基类),继承类的类叫做子类(派生类)

继承的定义:

继承的格式:

f4c0ea47a9124e44ac318b3e8624de86.png

 继承的关系:

43e4586ee18d4da0a67dcb2106702b4a.png

b01e62117ef944469412138440b219b7.png

1、继承方式和访问限定符都有三种权限
2、假如访问权限和继承方式结合,取权限小的访问方式
3、private中的不可见不是说不会被继承,是被继承下来我们不能够去使用它
4、假如我们想让外界访问不了基类成员,又想让派生类访问,那么就可以使用protect访问限定符修饰成员
5、假如我们不写继承方式,class创建出来的类默然是private,struct创建出来的类默然是public,但还是推荐手动加上继承方式
6、一般而言使用public继承方式,因为其他继承方式的可维护性低

基类和派生类的赋值转换(切片)

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用,但是基类不可以赋值给派生类,这种方式就叫做切片

person p = s;
person* ptr = &s;
person& ref  = s;
//都叫切片

继承中的作用域

1、基类和子类都拥有自己独立的作用域
2、当基类和派生类的成员变量同名时候,就会构成隐藏(也叫重定义),这个时候我们需要使用父类::成员名来进行访问,成员函数同名,也会构成隐藏
3、继承中尽量不要定义同名的成员

继承中的默认成员函数

构造函数:

1、假如不写自己成员,和类对象一样(内置类型值拷贝,自定义类型调用它的拷贝构造)

2、继承父类的成员,必须使用父类的构造函数初始化

3、父类成员不能够使用初始化列表初始化,需要用父类对象隐式传参初始化

class A
{
public:
	A(int x = 0)
		:_a(x)
	{}
	int _a;
};

class B : public A
{
public:
	B()
		:A(2)	//显示调用父类对象初始化
		,_b(0)
	{}
	int _b;
};

拷贝构造:

1、假如不写自己成员,和类对象一样(内置类型值拷贝,自定义类型调用它的拷贝构造)

2、继承父类的成员,必须使用父类的构造函数初始化

class A
{
public:
	//构造函数
	A(int x = 0)
		:_a(x)
	{}
	//拷贝构造
	A(A& a)
		:_a(a._a)
	{}
	int _a;
};

class B : public A
{
public:
	//构造函数
	B(int x = 0)
		:A(x)	//显示调用父类对象初始化
		,_b(0)
	{}
	//拷贝构造
	B(B& b)
		:A(b)	//显示调用父类对象初始化,进行切片
		, _b(b._b)
	{}
	int _b;
};

赋值运算符重载:

1、假如不写自己成员,和类对象一样(内置类型值拷贝,自定义类型调用它的拷贝构造)

2、显示调用父类的  父类::operator=,需要指定::类域

class A
{
public:
	//构造函数
	A(int x = 0)
		:_a(x)
	{}
	//拷贝构造
	A(const A& a)
		:_a(a._a)
	{}
	//赋值运算符重载
	A& operator=(const A& a)
	{
		_a = a._a;
		return *this;
	}
	int _a;
};

class B : public A
{
public:
	//构造函数
	B(int x = 0)
		:A(x)	//显示调用父类对象初始化
		,_b(0)
	{}
	//拷贝构造
	B(const B& b)
		:A(b)	//显示调用父类对象初始化,进行切片
		, _b(b._b)
	{}
	//赋值运算符重载
	B& operator=(const B& b)
	{
		A::operator=(b);	//显示调用父类的operator=,需要用::指定类域
		_b = b._b;
		return *this;
	}
	int _b;
};

析构函数:

1、子类的析构函数和父类的析构构成隐藏,由于后面多态的需要,析构函数会统一被处理成destructor(),需要指定调用
2、不需要显示调用父类析构函数,会自动调用,保证顺序

class A
{
public:
	//构造函数
	A(int x = 0)
		:_a(x)
	{}
	//拷贝构造
	A(const A& a)
		:_a(a._a)
	{}
	//赋值运算符重载
	A& operator=(const A& a)
	{
		_a = a._a;
		return *this;
	}
	~A()
	{
		cout << "~A" << endl;
	}
	int _a;
};

class B : public A
{
public:
	//构造函数
	B(int x = 0)
		:A(x)	//显示调用父类对象初始化
		, _b(0)
	{}
	//拷贝构造
	B(const B& b)
		:A(b)	//显示调用父类对象初始化,进行切片
		, _b(b._b)
	{}
	//赋值运算符重载
	B& operator=(const B& b)
	{
		A::operator=(b);	//显示调用父类的operator=,需要用::指定类域
		_b = b._b;
		return *this;
	}
	~B()
	{
		cout << "~B" << endl;
		//析构函数不能构成重载,因为编译器会自动变成destroctor,为了保持子类先析构的顺序,所有不需要我们自己调用父类的析构函数
	}
	int _b;
};

不想让类继承

方式一:将类的构造函数设置为私有

子类不能调用父类构造函数初始化来实例化对象,所以不能继承

缺点:我们自己也不能够实例化出对象

class A
{
private:
	//构造函数
	A(int x = 0)
		:_a(x)
	{}
	int _a;
};

方式二:c++11

类名后面添加final,就不能够被继承了

class A final
{
public:
	//构造函数
	A(int x = 0)
		:_a(x)
	{}
	int _a;
};

继承和友元的关系

派生类不会继承基类的友元关系

具体意思是基类中的友元函数不能使用派生类中的成员

继承和静态成员的关系

对于静态成员,如果被继承下来,那么整个继承类中都只会有这一份静态成员

继承中的菱形继承问题

class A
{};

class B:public A
{};

class C:public A
{};

class D:public B, public C
{};

假如D继承了B和C,B和C又同时继承了A,这个时候就会出现菱形继承情况

多继承会产生两个情况
1、二义性,不是到使用的是B继承下来的A的成员还是C继承下来的A的成员(这个时候需要类名::成员来指定使用的是哪个继承下来的变量)
2、数据冗余,存有两份A

继承里的虚继承

为了解决多继承中菱形继承的问题,c++创建了一种虚继承的方式来解决这种问题

class A
{};

class B:virtual public A
{};

class C:virtual public A
{};

class D:public B, public C
{};

在:后面腰部位置后面加上virtual关键字,来进行多继承,这样就只会创建一份A的成员了,在同时继承的两个成员旁边进行虚继承

 

继承和组合

在写两个类之间的关系的时候,尽量多使用组合,而不是继承

继承:

class Car
{};
class Baoma:public Car	//继承
{};

组合:组合就是在一个类中声明另一个类的对象

class Baoma:public Car	
{};
class Person	
{
public:
	Baoma b;    //组合就是在一个类中声明另一个类的对象
};

继承会破坏类的封装性,因为public继承可以访问protect成员,组合不会,还是只能访问public的成员

假如两个类是is-a的关系,还是可以使用继承

class Car
{};

class Baoma:public Car	//is-a的关系,所以可以用继承,Baoma是Car
{};

假如两个类是has-a的关系,更推荐使用组合

class Baoma
{};

class Person	//has-a的关系,可以用组合,Person有一辆宝马
{
public:
	Baoma b;
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一起慢慢变强

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值