中级C++:继承、默认成员函数回顾


继承

  • 基类和派生类有各自独立的类域
  • 当基类和派生类有名字相同的成员函数或成员变量时,派生类会隐藏基类的属性或函数 -或叫重定义
  • 建议自己定义时,尽量不要重名,如有重名时,派生类会优先在自己类域搜索。

  • 继承方式一般用公有继承。保护:类外对象不可访问。
    在这里插入图片描述
  • 基类中私有属性对任何继承方式的派生类都不可见:物理空间上是存在的,但不能使用。
  • 基类可以对派生类引用,指向派生类等, 指向、引用等拥有的只有派生类中基类的属性,称作切割。
    在这里插入图片描述
class peo  //称作基类/父类  派生类/子类
{
public:				
	peo(int t=10)//)				 
	{									
		_tel = t;					
		std::cout << "say my name: peo Construction" << std::endl;
	}											                                  
	peo(const peo& all)
	{
		_tel = all._tel;
		std::cout << "i didnt do anything" << std::endl;
	}
	/*peo& operator=(const peo& p)
	{
		_tel = p._tel;
		return *this;
	}*/
	~peo()							
	{
		std::cout << "~ peo " << std::endl;
	}
protected: 	      
	int _tel;
};

class she :public peo //一般用公有继承,保护是类外对象不可访问,不加限定符会默认为私有	
{									
public:	//不加限定符会默认为私有		

	she(const char* ch ="inthefuture",int t =1111)
		:_name(ch)		
		,peo(t)		
	{
		//如果写了,就显式的写一下  类名(成员变量)
	}
	she(const she& her)
		:peo(her)
		_name(her._name)
	{
		//如果没有显式的写基类的拷贝构造,则会调用基类的默认构造
	}

	she& operator=(const she& herr)		
	{
		if (this!=&herr)					//需要自己实现
		{								   //检测是否给自己赋值
			_name = herr._name;
			peo::operator=(herr);
		}
		return *this;
	}
	~she() {}   //如果写,这样就可以了。
protected:
	std::string _name;
};

int main()
{
	 she  s1("dd");
	 she s2(s1);
	 she s3;
	 s3 = s1;
		//指向、引用等拥有的只有派生类中基类的数据。
	/*p = s;
	peo& a = s;
	peo* po = &s;*/
	return 0;
}

派生类的默认成员函数

在这里插入图片描述


默认构造函数

派生类 构造函数,我们不写,编译器自动生成调用方式
继承的基类成员(属性)作为一个整体调用基类的构造函数进行初始化
自己的自定义类型成员调用它自己的默认构造函数
自己的内置类型成员不处理
  • 如果初始化列表没有写,也会按各类型的构造处理方式进行初始化。
  • 初始化顺序,只和声明顺序有关,因此基类会先初始。
    在这里插入图片描述

默认拷贝构造函数

派生类 拷贝构造函数,我们不写,编译器自动生成调用方式
继承的基类成员(属性)作为一个整体调用基类的默认拷贝构造函数进行拷贝
自己的自定义类型成员调用它自己的默认拷贝构造函数
自己的内置类型成员完成memcpy的浅拷贝(值拷贝)

如果没有显式的写基类的拷贝构造,则会调用基类的默认构造。

在这里插入图片描述
在这里插入图片描述


赋值重载函数

派生类 赋值重载函数,我们不写,编译器自动生成调用方式
继承的基类成员(属性)作为一个整体调用基类的默认赋值重载函数进行赋值
自己的自定义类型成员调用它自己的默认赋值重载函数
自己的内置类型成员完成值拷贝

关于赋值重载函数的精讲


析构函数

  • 编译器会对所有类的析构函数名做特殊处理,都会被处理成统一名字:destructor() ,因此基类派生类析构构成隐藏。
  • 派生类的析构函数结束后,会自动调用基类的析构函数。因为在栈上,后进先出。
对于析构函数,我们不写,编译器自动生成调用方式
继承的基类成员(属性)作为一个整体调用基类的析构函数
自己的自定义类型成员调用它自己的析构函数
自己的内置类型成员不处理(除非有动态申请的堆空间需要自己写)

继承的友元、静态成员

  • 友元关系不能继承,也就是说基类友元不能访问派生类私有和保护成员。
  • 基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。

单继承,多继承

  • 有且只有一个基类的继承,一脉单传的继承,叫单继承。

多继承:

  • 继承的顺序,就是声明的顺序。

  • 一个派生类有两个或以上直接基类。
    在这里插入图片描述

  • 菱形继承:数据冗余和二义性问题。
    在这里插入图片描述

虚拟继承

  • 偏移量记录着中层派生类到其基类属性的相对位置;原基类属性放着的是指针。
  • 拿到地址,解引用找到偏移量,然后加上偏移量,找到基类属性。只要虚拟继承了都是i这么操作的。
class Person
{
public :
 string _name ; / 姓名
};
class Student : virtual public Person
{
protected :
 int _num ; /学号
};

在这里插入图片描述

汇编操作:

在这里插入图片描述


继承和组合

  • 不要设计出菱形继承,多继承还好…
  • 继承堆基类的保护也可以访问,耦合度高,容易受基类影响 is -a 是
  • 组合就不行了;耦合度低 has - a 有

形象化:

  • 继承:跟团一起走
  • 组合:跟团自由行
    在这里插入图片描述
    在这里插入图片描述

寄语

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值