C++七:类的继承

一、什么是继承?

        儿子从爸爸里面继承一些 资源过来。这样儿子就很快乐,儿子就不需要实现父亲的(功能)资源。

二、类的继承

继承 : 子类从父类中,继承数据成员和功能函数,继承后这些数据和函数可以在子类直接使用。
作用: 1. 提高代码的复用性, 2. 有利于软件版本的升级。

提示:子类 与 父类 必须存在正常的逻辑关系,不要滥用继承。

1、继承的语法

class 类名 : 继承方式 父类名(基类名)
{
        //设计子类(派生类)的函数接口或成员
}
// 继承的 demo: 通过一个人派生出 警察
// 设计基类(父类)
class people
{
        private :
                char name [ 100 ];
                int age ;
};

// 派生出 警察类 派生类 (子类)
class police : public people
{
        public :
                void CatchThief ()
                {
                        cout << " 警察抓住一个小偷啦 " << endl ;
                }
}
#include <iostream>

using namespace std;

//设计基类(父类)
class people
{
	private:
		char name[100];		//姓名
		int age;			//年龄
		char sex[100];		//性别
};

//派生出 警察类 派生类(子类)
class police : public people
{
	public:
		void CatchThief()
		{
			cout << "Police catch thief..." << endl;
		}
};

int main()
{
	police p;
	p.CatchThief();
	
	return 0;
}

编译运行结果如下所示:

练习1:定义一个动物类,包含 年龄和颜色,通过动物类,派生出 猫类 和 狗类,再设计它们特有的功能接口。

//定义一个动物类,包含 年龄和颜色,通过动物类,
//派生出 猫类 和 狗类,再设计它们特有的功能接口。
#include <iostream>

using namespace std;

//设计基类(父类)
class Animal
{
	private:
		int age;			//年龄
		char color[100];	//颜色
};

//派生出 猫类 派生类(子类)
class Cat : public Animal
{
	public:
		void CatchMice()
		{
			cout << "Cat catch mice..." << endl;
		}
};

//派生出 狗类 派生类(子类)
class Dog : public Animal
{
	public:
		void GetFrisbe()
		{
			cout << "Dog get frisbe..." << endl;
		}
};

int main()
{
	Cat c;
	c.CatchMice();
	
	Dog d;
	d.GetFrisbe();
	
	return 0;
}

编译运行,结果如下:

2、继承的存储空间问题

#include <iostream>

using namespace std;

//设计基类(父类)
class base
{
	private:
		int date;
};

//设计一个派生类(子类)
class new_base : public base
{
	private:
		float f_date;
};

int main()
{
	cout << "new_base size: " << sizeof(new_base) << endl;
	
	return 0;
}

编译运行,结果如下所示:

        可见,new_base 的空间大小是8,因为new_base是继承了base的,所以new_base里面还包含了base的空间大小。

分析继承的空间问题:

3、继承的权限问题(重点)

回顾类的属性权限
class base
{
        public : // 公共区 : 类内,类外,子类 都可以访问 (所有人都可以访问)
        
        protected : // 保护区 : 类内,子类 可以访问 , 类外不能访问
        
        private : // 私有区 : 类内可以访问。
};

详解:

#include <iostream>

using namespace std;

//设计基类(父类)
class base
{
	public:
		int a;
		//set_base 是 base的内部函数,所以属于类内访问,
		//可以访问当前类的一切数据成员!!
		void set_base()
		{
			a = 100;
			b = 200;
			c = 300;
		}
	protected:
		int b;
	private:
		int c;
};

//设计一个派生类(子类)
class new_base : public base
{
	public:
		//new_base 是 base 的派生类(子类),子类可以访问
		//父类的共用和保护成员
		void set_newbase()
		{
			a = 400;
			b = 500;
			//c = 600;	//不可以访问父类的私有成员
		}
};

int main()
{
	//定义一个 new_base 对象
	new_base a;
	//main函数 与 base 和 new_base 都没有任何关系,属于类外访问。
	//类外只能访问共用成员
	a.a = 100;
	//a.b = 200;	//不可以访问
	//a.c = 300;	//不可以访问
	
	return 0;
}

那么,子类要如何去访问基类的私有成员?

答:在基类(保护区或共用区)中开发接口,提供子类访问。
#include <iostream>

using namespace std;

//设计基类(父类)
class base
{
	protected:
		//set_base 是 base的内部函数,所以属于类内访问,
		//可以访问当前类的一切数据成员!!
		void set_base(int a, int b)
		{
			this->a = a;
			this->b = b;
		}
		
		void show_base()
		{
			cout << "a: " << a << endl;
			cout << "b: " << b << endl;
		}

	private:
		int a;
		int b;
};

//设计一个派生类(子类)
//new_base 包含了两个数据成员,分配是int a,int b
class new_base : public base
{
	public:
		//设置数据
		void set_data(int a, int b)
		{
			this->set_base(a,b);
		}
		
		//显示数据
		void show_data()
		{
			this->show_base();
		}
};

int main()
{
	//定义一个 new_base 对象
	new_base a;
	a.set_data(100,200);
	a.show_data();
	
	return 0;
}

编译运行,结果如下所示:

练习2:设计一个角色类,包含私有成员, 力量,敏捷,智力,通过角色类派生出,法师类 和 战士类。定义派生类的对象,并调用派生类的接口,初始化角色类的数据成员,并输出。

#include <iostream>

using namespace std;

//设计基类(父类)
//角色类
class Role
{
	protected:
		void set_role(int force, int agility, int intelligence)
		{
			this->force = force;
			this->agility = agility;
			this->intelligence = intelligence;
		}
		
		//获取属性接口
		int get_Force()
		{
			return force;
		}
		
		int get_Agility()
		{
			return agility;
		}
		
		int get_Intelligence()
		{
			return intelligence;
		}
		
		void show_role()
		{
			cout << "force: " << force << endl;
			cout << "agility: " << agility << endl;
			cout << "intelligence: " << intelligence << endl;
		}

	private:
		int force;			//力量
		int agility;		//敏捷
		int intelligence;	//智力
};

//设计派生类(子类)

//法师类
class Master : public Role
{
	public:
		//设置数据
		void set_master(int force, int agility, int intelligence)
		{
			this->set_role(force,agility,intelligence);
		}
		
		//显示数据
		void show_data()
		{
			this->show_role();
		}
		
		//攻击接口
		void attack()
		{
			//冰冻攻击
			cout << "Freezing attack: " << this->get_Force() << endl;
		}
};

//战士类
class Soldier : public Role
{
	public:
		//设置数据
		void set_soldier(int force, int agility, int intelligence)
		{
			this->set_role(force,agility,intelligence);
		}
		
		//显示数据
		void show_data()
		{
			this->show_role();
		}
		
		//攻击接口
		void attack()
		{
			//认真一拳
			cout << "Give a serious punch: " << this->get_Force() << endl;
		}
};

int main()
{
	//定义一个 Master 对象
	Master master;
	master.set_master(50,78,120);
	master.show_data();
	master.attack();
	
	//定义一个 Soldier 对象
	Soldier soldier;
	soldier.set_soldier(99,45,72);
	soldier.show_data();
	soldier.attack();
	
	return 0;
}

编译运行,结果如下所示:

4、继承中的构造函数

在继承中所有构造函数都会执行, 先执行基类 构造,再执行派生类 构造。
#include <iostream>

using namespace std;

//设计基类(父类)
class base
{
	public:
		base(int a)
		{
			cout << "base(): " << a << endl;
		}
		
		base()
		{
			cout << "base()" << endl;
		}
};

//设计一个派生类(子类)
class new_base : public base
{
	public:
		new_base()
		{
			//在创建 new_base 对象时,先调用 base 的构造函数,但是base的构造函数是一个有参数的。
			//所以new_base无法调用得到。
			//解决方法:
			//1、添加一个无参的构造函数
			//2、基类不写构造函数,使用系统自动生成的默认构造函数
			
			cout << "new_base()" << endl;
		}
};

int main()
{
	//创建一个new_base对象
	new_base a;
	
	return 0;
}

编译运行,结果如下所示:

5、继承中的参数列表初始化

#include <iostream>

using namespace std;

//设计基类(父类)
class base
{
	public:
		base(int d)	//base的构造
		{
			date = d;
		}
		
		int date;
};

//设计一个派生类(子类)
class new_base : public base
{
	public:
		new_base(int d,int d1):base(d1)
		{
			date = d;
		}
		
		int date;
};

int main()
{
	//创建一个new_base对象
	new_base tmp(100,200);
	
	cout << tmp.date << endl;
	cout << tmp.base::date << endl;
	
	return 0;
}

编译运行,结果如下所示:

分析如下:

练习3:完成下来类的数据初始化,使用参数列表的方式

class base
{
        public :
                int a ;
                char buf [ 1024 ];
};
class new_base : public base
{
        public :
                short date ;
                float b ;
};
// 设计参数列表初始化,初始化 new_base 中的所有数据成员
#include <iostream>
#include <string.h>

using namespace std;

//设计基类(父类)
class base
{
	public:
		base(int a,const char *buf)	//base的构造
		{
			this->a = a;
			strcpy(this->buf,buf);
		}
		
		int a;
		char buf[1024];
};

//设计一个派生类(子类)
//现在new_base中含有4个数据成员,两个自己的,两个继承过来的
//所以构造函数需要传递4个参数
class new_base : public base
{
	public:
		new_base(int a,const char *buf,short date,float b):base(a,buf)
		{
			this->date = date;
			this->b = b;
		}
		
		short date;
		float b;
};

int main()
{
	//创建一个new_base对象
	new_base tmp(100,"Xiaowu",12,3.14);
	
	//测试显示数据
	cout << tmp.a << endl;
	cout << tmp.b << endl;
	cout << tmp.buf << endl;
	cout << tmp.date << endl;
	
	return 0;
}

编译运行,结果如下所示:

6、继承中的析构函数

在继承中所有析构函数都会执行,先执行 派生类 析构,再执行基类 析构
#include <iostream>
#include <string.h>

using namespace std;

//继承中的析构函数:执行顺序与构造函数是相反的
//在继承中所有的析构函数都会执行,先执行派生类析构,再执行基类析构

//设计基类(父类)
class base
{
	public:
		~base()	//base的析构
		{
			cout << "~base()" << endl;
		}
};

//设计一个派生类(子类)
class new_base : public base
{
	public:
		~new_base()
		{
			cout << "~new_base()" << endl;
		}
		
};

int main()
{
	//创建一个new_base对象
	new_base a;
	
	return 0;
}

编译运行,结果如下:

7、继承中的拷贝构造问题

#include <iostream>
#include <string.h>

using namespace std;

//继承中的析构函数:执行顺序与构造函数是相反的
//在继承中所有的析构函数都会执行,先执行派生类析构,再执行基类析构

//设计基类(父类)
class base
{
	public:
		base()
		{
			p = new char[1024];	//分配一块堆空间
		}
		~base()	//base的析构
		{
			delete []p;	//释放空间
			cout << "~base()" << endl;
		}
	private:
		char *p;
};

//设计一个派生类(子类)
class new_base : public base
{
	public:
		new_base()
		{
			q = new int;	//分配一块堆空间
		}
		~new_base()
		{
			delete q;	//释放空间
			cout << "~new_base()" << endl;
		}
	private:
		int *q;
};

int main()
{
	//创建一个new_base对象
	new_base a;
	
	//调用拷贝构造函数
	new_base b = a;
	
	return 0;
}

编译运行,将会出现问题,分析如下所示:

重写继承中的拷贝构造函数:

#include <iostream>
#include <string.h>

using namespace std;

//继承中的析构函数:执行顺序与构造函数是相反的
//在继承中所有的析构函数都会执行,先执行派生类析构,再执行基类析构

//设计基类(父类)
class base
{
	public:
		base()
		{
			p = new char[1024];	//分配一块堆空间
			cout << "base() &p" << (void *)p << endl;
		}
		
		base(int size)
		{
			p = new char[size];	//分配一块堆空间
			cout << "base() &p" << (void *)p << "size" << size << endl;
		}
		
		~base()	//base的析构
		{
			cout << "~base() &p: " << (void *)p << endl;
			delete []p;	//释放空间
		}
	private:
		char *p;
};

//设计一个派生类(子类)
class new_base : public base
{
	public:
		new_base()
		{
			q = new int;	//分配一块堆空间
			cout << "&q" << (void *)q << endl;
		}
		
		//重写new_base的拷贝构造函数
		new_base(new_base &t):base(4096)
		{
			cout << "new_base(new_base &t)" << endl;
			//让 q 指向新的堆空间
			q = new int;
			//打印分配的堆空间地址
			cout << "&q: " << (void *)q << endl;
			
			//让 p 也指向新的堆空间
			//p = new char[1024];	//不可以调用基类的私有成员
		}
		
		~new_base()
		{
			cout << "~new_base() &q: " << (void *)q << endl;
			delete q;	//释放空间
		}
	private:
		int *q;
};

int main()
{
	//创建一个new_base对象
	new_base a;
	
	//调用 new_base 拷贝构造函数
	//注意:不管是拷贝构造函数还是构造函数都是 先调用基类,再调用派生类
	//而析构函数是 先调用派生类,再调用基类
	new_base b = a;
	
	return 0;
}

编译运行,结果如下所示:

练习4:完成下述类中的,构造函数,析构函数,拷贝构造函数等接口。

//设计基类(父类)

class base

{

private:

int size;

char *p; //指向一块 size 大小的堆空间

};

//设计一个派生类(子类)

class new_base : public base

{

private:

int *q; //指向一块 int 类的堆空间

};

#include <iostream>
#include <string.h>

using namespace std;

//设计基类(父类)
class base
{
	private:
		int size;
		char *p;	//指向一块 size 大小的堆空间
	public:
		//base的带参构造函数
		base(int size):size(size)
		{
			p = new char[this->size];
			//打印分配的堆空间及大小
			cout << "base: (p)" << (void *)p << ",size: " << this->size << endl;
		}
		//重写base的拷贝构造函数
		base(base &t)
		{
			this->size = t.size;
			this->p = new char[t.size];
			cout << "base(base &t): (p)" << (void *)this->p << ",size: " << this->size << endl;
		}
		//base 的析构函数
		~base()
		{
			//打印释放的堆空间地址
			cout << "~base: (p)" << (void *)p << endl;
			delete []p;
		}
};

//设计一个派生类(子类)
class new_base : public base
{
	private:
		int *q;		//指向一块 int 类的堆空间
	public:
		//new_base的构造函数
		new_base(int size):base(size)
		{
			q = new int;
			cout << "new_base: (q)" << (void *)q << endl;
		}
		//重写拷贝构造函数
		new_base(new_base &t):base(t)
		{
			this->q = new int;
			cout << "new_base(new_base &t): (q)" << (void *)this->q << endl;
		}
		//new_base的析构函数
		~new_base()
		{
			cout << "~new_base: (q)" << (void *)q << endl;
			delete q;
		}
};

int main()
{
	cout << "------------base-------------" << endl;
	//创建一个base对象
	base a(1024);
	//基类进行调用拷贝构造
	base b = a;
	
	cout << "----------new_base-----------" << endl;
	//创建一个base对象
	new_base c(1024);
	//基类进行调用拷贝构造
	new_base d = c;
	
	return 0;
}

编译运行,结果如下:

8、继承中的赋值问题

提示:必须拥有情缘关系的类才符合这种关系
1、 派生类可以赋值给基类, 基类不可以赋值给派生类。 (大的可以覆盖小的,小的不能覆盖大的)
2、 基类可以引用派生类, 派生类不能引用基类。
#include <iostream>

using namespace std;

//设计基类(父类)
class base
{
	public:
		base(int date):date(date)
		{
			
		}
		void show_base()
		{
			cout << "date: " << date << endl;
		}
		
	private:
		int date;
};

//设计一个派生类(子类)
class new_base : public base
{
	public:
		new_base(int date,float a):base(date),a(a)
		{	
			
		}
		void show()
		{
			this->show_base();
			cout << "a: " << a << endl;
		}
	private:
		float a;
};

int main()
{
	base a(100);
	new_base b(10086,3.14);
	a.show_base();
	
	a = b;	//派生类 赋值给 基类 (大的 可以覆盖 小的)
	a.show_base();
	
	//b = a;	//基类 赋值给 派生类,不可以 (小的 不可以覆盖 大的)
	
	//基类 引用 派生类  可以
	base &qq = b;
	qq.show_base();
	
	//派生类 引用 基类  不可以
	//new_base &ww = a;
	
	return 0;
}

编译运行,结果如下:

分析:

9、继承方式

(1)不管哪种继承方式都不会影响,子类(派生类)对 父 类 (基类)的访问权限 (保护,公有)
(2)不同的继承方式,就相当于把 父类(基类),放到子 类(派生类)的那个区 .
(3)类外只能访问,基类的公共继承的公有成员

10、继承方式的分类

class 派生类 : public 基类 -> 公有继承,相 当于把 基类放到 派生类的公有区
class 派生类 : protected 基类 -> 保护继承,相等于把 基类放到 派生类的保护区
class 派生类 : private 基类 -> 私有继承,相 当于把 基类放到 派生类的私有区
#include <iostream>

using namespace std;

class base
{
	public:
		//公有  所有人都可以访问
		int a;
	protected:
		//保护  类内 和 子类 可以访问
		int b;
	private:
		//私有  类内 可以访问
		int c;
};

//公共继承
class pub_base:public base
{
	public:
		//子类访问父类的成员
		void set_base()
		{
			a = 100;
			b = 200;
			//c = 300; 私有成员只有类内可以访问
		}
};

//保护继承
class pro_base:protected base
{
	public:
		//子类访问父类的成员
		void set_base()
		{
			a = 100;
			b = 200;
			//c = 300; 私有成员只有类内可以访问
		}
};

//私有继承
class pri_base:private base
{
	public:
		//子类访问父类的成员
		void set_base()
		{
			a = 100;
			b = 200;
			//c = 300; 私有成员只有类内可以访问
		}
};

//由上面可以看出,不管哪种继承方式,都不会影响 子类 对 父类 的访问权限

int main()
{
	//类外访问
	//类外 只能访问,基类的公共继承的公有成员
	pub_base a;
	a.a = 100;
	//a.b = 200;	不可以访问
	//a.c = 300;
	
	pro_base b;
	//b.a = 100;	不可以访问
	//b.b = 200;	不可以访问
	//b.c = 300;	不可以访问
	
	pri_base c;
	//c.a = 100;	不可以访问
	//c.b = 200;	不可以访问
	//c.c = 300;	不可以访问
	
	return 0;
}

不同的继承方式的访问权限

继承的方式练习题

11、私有继承 和 保护继承 的区别

私有和保护继承是应用在多继承中的。
如果不想 派派生类,访问基类的成员,则使用 私有继承。
如果想 派派生类,访问基类的成员,则使用保护 或 共用继承。
#include <iostream>

using namespace std;

//基类
class base
{
	public:
		char buf[100] = "1000,0000$";	//1000万
};

//派生类
/*
//公有继承
class new_base : public base
{
	//所有人都可以访问这1000万
};
*/

//保护继承
class new_base : protected base
{
	//只有自己 和 子类 可以访问 
};

//私有继承
class new_base1 : private base
{
	//只有自己可以访问,据为己有
};

//派派生类
class new_new_base : public new_base
{
	public:
		void pf()
		{
			cout << buf << endl;	//可以访问基类的成员
		}
};

class new_new_base1 : public new_base1
{
	public:
		void pf()
		{
			//cout << buf << endl;	//不可以访问基类的成员
			cout << "new_new_base1 pf()" << endl;
		}
};

int main()
{
	new_new_base a;
	a.pf();
	
	new_new_base1 b;
	b.pf();
	
	return 0;
}

编译运行,结果如下所示:

  • 13
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值