C++ 继承

继承

1. 概念:

  • 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特
    性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,
    体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。

2. 继承格式

class Person
{
	public:
	void Print()
	{
	cout << "name:" << _name << endl;
	cout << "age:" << _age << endl;
	}
	protected:
	string _name = "peter"; // 姓名
	int _age = 18; // 年龄
};
	  //子类(派生类)     //父类(基类)
class Student : public Person   //继承格式
{
	protected:int _stuid; // 学号
};
	//子类(派生类)     //父类(基类)
class Teacher : public Person
{
	protected:
	int _jobid; // 工号
};
int main()
{
	Student s;
	Teacher t;
	s.Print();
	t.Print();
	return 0;
}

3. 继承基类成员访问方式的变化
在这里插入图片描述
4. 基类和派生类对象赋值转换

  • 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。也称对象的切片。
  • 基类对象不能赋值给派生类对象
class Base
{
public:
	void show()
	{
		cout << "Base::show()" << endl;
	}
private:
	int m_b = 0;
};

class D : public Base
{
	void show()
	{
		cout << "D::show()" << endl;
	}
private:
	int m_d = 0;
};
void main()
{
	D d;
	Base b;
	//赋值兼容规则  
	//3种方式
	b = d;    //子类对象直接给父类对象赋值
	Base *pb = &d;  //子类对象的地址可以直接给父类指针赋值
	Base &rb = d;   //子类对象可以赋值(初始化)父类的引用
}

5. 继承中的作用域

  • 在继承体系中基类和派生类都有独立的作用域。
  • 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
  • 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  • 注意在实际中在继承体系里面最好不要定义同名的成员。

5. 派生类默认成员函数


/*
1. 子类对象创建调用构造方法,首先要调用父类的构造方法,然后在调用子类
2. 子类对象创建通过拷贝构造方法,首先要调用父类的拷贝构造方法,然后在调用子类
3. 子类对象创建通过赋值语句方法,首先要调用父类的赋值语句方法,然后在调用子类

4. 子类对象析构,先调用子类, 在调用父类
5. 子类对象初始化先调用父类在调用子类构造
6. 子类对象的析构清理先调用子类析构再调用父类的析构
*/

class Base
{
public:
	Base(int b = 0) : m_b(b)
	{
		cout << "Base::Base()" << endl;
	}
	Base(const Base &b)
	{
		cout << "Base::Base(const Base &b)" << endl;
	}
	Base& operator=(const Base &b)
	{
		cout << "Base::operator=(const Base &b)" << endl;
		m_b = b.m_b;
		return *this;
	}
private:
	int m_b;
};

class D : public Base
{
public:
	//显示调用父类构造函数
	D(int d = 0) : m_a(d), Base(10)
	{
		cout << "D::D()" << endl;
	}

	D(const D &b)
	{
		cout << "D::D(const D &b)" << endl;
	}

	D& operator=(const D &d)
	{
		cout << "D::operator=(const D &b)" << endl;
		m_a = d.m_a;
		return *this;
	}
private:
	int m_a;
};


void main()
{
	//基础构造
	D d(100);

	//拷贝构造
	D d1 = d;

	//赋值语句
	D d2;
	d2 = d;
}
/*
多继承
class D : public Base1, public Base2, public Base3;	
调用构造方法的顺序 Base1, Base2, Base3  D
析构顺序 ~D, ~Base3, ~Base2, ~Base1
*/

6. 基类友元函数

  • 友元关系不能继承(不能传递),也就是说父类友元不能访问子类私有和保护成员。因此要使用基类的友元函数,需要在派生类中声明。

7. static 静态成员

  • 父类定义了static静态成员,则整个继承体系里面只有一个这样的成员。 无论派生出多少个子类,都只有一个static成员实例。
  • static成员必须在类外部进行初始化。

8. 菱形继承

在这里插入图片描述

#include<iostream>
using namespace std;

/*
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。
但是其他地方,不要随意使用。
*/
/*
在底层中, 正常继承父类, 子类数据空间的前面会将父类的成员的数据空间
若 virtual继承父类,  而子类数据空间的前面 将会是 父类的成员 的数据空间 的偏移量, 会按照虚基表的形式存储。
*/

class A
{
public:
	int m_a = 1;
};

class B : virtual public A  // A是虚基类, 虚父类
{
public:
	int m_b = 2;
};

class C : virtual public A
{

public:
	int m_c = 3;
};

class D : public B, public C
{
public:
	int m_d = 4;
};

void main()
{
	D d;
	d.m_d = 40;
	d.m_c = 30;
	d.m_b = 20;


	//因为a既来自于 类B或C, 又可能来自于类A, 因此产生二义性
//	d.m_ 10; //错误无法访问,二义性, 如果不虚拟继承
	//可以这样修改访问, 不同的空间
	//d.B::m_a = 100;
	//d.C::m_a = 200;


	//采用虚拟继承
	//此时无论来自哪里的a其所指空间都是相同的。
	d.m_a = 100;
}

9. 继承和组合

  • public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。 继承是比如基类是动物,那么人,鸟 都继承动物,是动物的一种。此时就要使用继承。
  • 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。组合就是比如一个大类汽车,其包含4个轮子,4个门,这都是汽车的一部分,都是汽车的成员。
  • 优先使用对象组合,而不是类继承 。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值