复习重难点-继承和派生1

3.1继承概念

面向对象程序设计有4个主要特点:抽象、封装、继承和多态性。我们已经讲解了类和对象,了解了面向对象程序设计的两个重要特征一数据抽象与封装,已经能够设计出基于对象的程序,这是面向对象程序设计的基础。

要较好地进行面向对象程序设计,还必须了解面向对象程序设计另外两个重要特征——继承性和多态性。本章主要介绍有关继承的知识,多态性将在后续章节中讲解。

继承性是面向对象程序设计最重要的特征,可以说,如果没有掌握继承性,就等于没有掌握类和对象的精华,就是没有掌握面向对象程序设计的真谛。

3.1.1类之间的关系

has-A,uses-A 和 is-A

has-A   包含关系,用以描述一个类由多个“部件类”构成。实现has-A关系用类成员表示,即一个类中的数据成员是另一种已经定义的类。

uses-A  一个类部分地使用另一个类。通过类之间成员函数的相互联系,定义友员或对象参数传递实现。

is-A   机制称为“继承”。关系具有传递性,不具有对称性。

3.1.2继承关系举例

万事万物中皆有继承,是重要的现象

两个案例:1)植物继承图;2)程序员继承图

3.1.3 继承相关概念

3.1.4 派生类的定义

注意:C++中的继承方式(public、private、protected)会影响子类的对外访问属性。

3.1.5 继承重要说明

1、子类拥有父类的所有成员变量和成员函数

4、子类可以拥有父类没有的方法和属性

 

2、子类就是一种特殊的父类

3、子类对象可以当作父类对象使用

3.2派生类的访问控制

派生类继承了基类的全部成员变量和成员方法(除了构造和析构之外的成员方法),但是这些成员的访问属性,在派生过程中是可以调整的。

3.2.1单个类的访问控制

1、类成员访问级别(public、private、protected)

2、思考:类成员的访问级别只有public和private是否足够?

3.2.2不同的继承方式会改变继承成员的访问属性

1)C++中的继承方式会影响子类的对外访问属性

public继承:父类成员在子类中保持原有访问级别

private继承:父类成员在子类中变为private成员

protected继承:父类中public成员会变成protected

                            父类中protected成员仍然为protected

                            父类中private成员仍然为private

2)private成员在子类中依然存在,但是却无法访问到。不论何种方式继承基类,派生类都不能直接使用基类的私有成员。

3)C++中子类对外访问属性表

 

父类成员访问级别

 

public

proteced

private

public

public

proteced

private

proteced

proteced

proteced

private

private

private

private

Private

 

4)继承中的访问控制

//demo_01继承的基本语法
#include <iostream>
using namespace std;

class parent
{
public:
	void print()
	{
		cout << "a:" << a << "b:" << b << endl;
	}
	int b;

private:
	int a;
};
//public继承:父类成员在子类中保持原有访问级别
//private继承:父类成员在子类中变为private成员
//protected继承:父类中public成员会变成protected
//父类中protected成员仍然为protected
//父类中private成员仍然为private

class Child:public parent
{
public:


private:
	int c;
};

int main()
{
	Child c1;

	//c1.a = 1;//为基类的
	c1.b = 3;
	//c1.c = 1;成员 "Child::c"  不可访问


	system("pause");
	return 0;
}

demo_02单个类的访问控制.cpp

#include <iostream>
using namespace std;
/*
C++中的继承方式(public、private、protected)会影响子类的对外访问属性
判断某一句话,能否被访问
1)看调用语句,这句话写在子类的内部、外部
2)看子类如何从父类继承(public、private、protected)
3)看父类中的访问级别(public、private、protected)
*/

//public 修饰的成员变量 在类的内部、外部都能使用
class Parent1
{
public:
	int a;//老爹的名字
protected:
	int b;//老爹的银行卡密码 仅仅允许家族人内部知道,外面人不给知道==>protected在公用继承后,在类的内部能使用,外部不能被使用,protected在protected继承后,还是protected,protected在private继承后private
private:
	int c;//相当于 老爹的情人
};

class Child1:public Parent1
{
public:
	void UseVar()
	{
		a = 0;	//ok			public 在子类中,类外都能访问
		b = 0;	//ok		protected:在子类中能访问,但是在类外不能访问
		//c = 0; //err		private:在子类中,在类外都不能访问
	
	}
protected:
private:

};
//公用继承
int main0201()
{
	Child1 c1;
	c1.a = 0;	 //ok			public 在子类中,类外都能访问
	//c1.b = 0;	 //err		protected:在子类中能访问,但是在类外不能访问
	//c1.c = 0;	  //err		private:在子类中,在类外都不能访问

}

//protected: 修饰的成员变量方法,在类的内部使用 ,在继承的子类中可用 ;其他 类的外部不能被使用
//protected 关键字修饰的成员变量和成员函数,是为了在家族中使用,是为了继承
/*
protected继承:父类中public成员会变成protected
父类中protected成员仍然为protected
父类中private成员仍然为private


*/
class Child2 :protected Parent1
{
	void UseVar()
	{
		a = 0;	//ok		父类中public成员会变成protected:	 在子类中能访问,类外不能访问
		b = 0;	//ok		父类中protected成员仍然为protected:  在子类中能访问,但是在类外不能访问
		//c = 0; //err		父类中private成员仍然为private:      在子类中,在类外都不能访问

	}


};
void main0202()
{
	Child2 c2;
	//c2.a = 0;//ok		父类中public成员会变成protected:	 在子类中能访问,类外不能访问
	//c2.b = 1;//ok		父类中protected成员仍然为protected:  在子类中能访问,但是在类外不能访问
	//c2.c = 2; //err	父类中private成员仍然为private:      在子类中,在类外都不能访问

}
//private: 修饰的成员变量方法 只能在类的内部使用 不能在类的外部
class Child3 :private Parent1
{
	void UseVar()
	{
		a = 0;	//ok		父类中public成员会变成private:	 1.写在子类中,2.看从父类里private继承过来的,3.父类里访问级别是public 故在子类中能访问,类外不能访问
		b = 0;	//ok		父类中protected成员仍然为protected:  1.写在子类中,2.看从父类里private继承过来的,3.父类里访问级别是protected 故在子类中能访问,但是在类外不能访问
		//c = 0; //err		父类中private成员仍然为private:      在子类中,在类外都不能访问

	}
};
//记住private继承过来的,类外都不能使用
void main()
{
	Child2 c2;
	//c2.a = 0;//ok		父类中public成员会变成private:	 1.写在子类中,2.看从父类里private继承过来的,3.父类里访问级别是public 故在子类中能访问,类外不能访问
	//c2.b = 1;//ok		父类中protected成员仍然为protected:  1.写在子类中,2.看从父类里private继承过来的,3.父类里访问级别是protected 故在子类中能访问,但是在类外不能访问
	//c2.c = 2; //err		父类中private成员仍然为private:      在子类中,在类外都不能访问

}

3.2.3“三看”原则

C++中的继承方式(public、private、protected)会影响子类的对外访问属性     

判断某一句话,能否被访问

         1)看调用语句,这句话写在子类的内部、外部

         2)看子类如何从父类继承(publicprivateprotected       

         3)看父类中的访问级别(publicprivateprotected

3.2.3派生类类成员访问级别设置的原则

         思考:如何恰当的使用public,protected和private为成员声明访问级别?

1、需要被外界访问的成员直接设置为public

2、只能在当前类中访问的成员设置为private

3、只能在当前类和子类中访问的成员设置为protectedprotected成员的访问权限介于publicprivate之间。

3.2.4综合训练

练习:

public继承不会改变父类对外访问属性;

private继承会改变父类对外访问属性为private;

protected继承会部分改变父类对外访问属性。

结论:一般情况下class B : public A


//demo_03派生类访问控制综合训练.cpp
//类的继承方式对子类对外访问属性影响

#include <cstdlib>
#include <iostream>

using namespace std;

class A
{
private:
	int a;
protected:
	int b;
public:
	int c;

	A()
	{
		a = 0;		b = 0;		c = 0;
	}

	void set(int a, int b, int c)
	{
		this->a = a;		this->b = b;		this->c = c;
	}
};
//公用继承
class B : public A
{
public:
	void print()
	{
		//cout<<"a = "<<a; //err private还是private,只能在父类类内访问
		cout << "b = " << b; //ok protected还是protected 基类子类中访问,类的外部不能访问
		cout << "c = " << endl; //ok public还是public 都能访问
	}
};
/*
1、需要被外界访问的成员直接设置为public
2、只能在当前类中访问的成员设置为private
3、只能在当前类和子类中访问的成员设置为protected,protected成员的访问权限介于public和private之间。

*/
//保护继承
class C : protected A
{
public:
	void print()
	{
		//cout<<"a = "<<a;    //err  父类中private还是private 只能在基类中访问的成员
		cout << "b = " << b; //ok    父类中protected还是protected 能在基类和子类中访问,类外部不能访问

		cout << "c = " << endl; //ok 父类中public变成protected 能类的内部访问,不能外部访问
	}
};
//private私有继承
class D : private A
{
public:
	void print()
	{
		//cout<<"a = "<<a; //err 父类中private还是private,只能在基类中访问
		cout << "b = " << b << endl;  //ok protected 变成private,能在类内部访问
		cout << "c = " << c << endl; // ok 只能在子类的内部访问了
	}
};

int main()
{

	A aa;
	B bb;
	C cc;
	D dd;

	aa.c = 100;  //1.看写在类的外部,2.看类中的访问级别,c是public,故可以类的外部访问
	bb.c = 100; // ok 1.看写在类的外部 2.看子类是从父类中公用继承过来的,c在父类中访问级别是public,总体还是public,故可以在类的外部访问
	//cc.c = 100; // err  1.看写在类的外部 2.看子类是从父类中protected继承过来的,c在父类中访问级别是public,总体还是protected,不可以在类的外部访问
	//dd.c = 100;  // err1.看写在类的外部 2.看子类是从父类中private继承过来的,不可以在类的外部访问

	aa.set(1, 2, 3); //ok 1.看写在类外的,2.看类中的访问级别public 
	bb.set(10, 20, 30); //ok1.看写在类外的,2.看类中的访问级别public 
	//cc.set(40, 50, 60); // err 1.看写在类外的,2.看子类从父类中保护继承过来的,故不能访问
	//dd.set(70, 80, 90);  //err1.看写在类外的,2.看子类从父类中私有继承过来的,故不能访问


	bb.print(); //ok 1.看写在类外的,2.不是子类就不看,直接看父类的访问级别是public,肯定就可以类外访问
	cc.print(); //ok 1.看写在类外的,2.不是子类就不看,直接看父类的访问级别是public,肯定就可以类外访问

	system("pause");
	return 0;
}

3.3.1类型兼容性原则

类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。类型兼容规则中所指的替代包括以下情况:

子类对象可以当作父类对象使用

子类对象可以直接赋值给父类对象

子类对象可以直接初始化父类对象

父类指针可以直接指向子类对象

父类引用可以直接引用子类对象

在替代之后,派生类对象就可以作为基类的对象使用,但是只能使用从基类继承的成员。

类型兼容规则多态性的重要基础之一。

//demo_05类型兼容性原则.cpp
/*
兼容规则中所指的替代包括以下情况:
子类对象可以当作父类对象使用
子类对象可以直接赋值给父类对象
子类对象可以直接初始化父类对象
父类指针可以直接指向子类对象
父类引用可以直接引用子类对象
*/

#include <iostream>
using namespace std;

class Parent
{
public:
	void prinT()
	{
	
		cout << "我是父类" << endl;
	}

	Parent()
	{
		cout << "Parent 构造函数" << endl;
	}

	Parent(const Parent &obj)
	{
		cout << "Parent 拷贝构造函数" << endl;
	}
private:

};
//class Child:Parent class 默认私有继承
class Child:public Parent
{
public:
	void printC()
	{
		cout << "我是儿子" << endl;
	}
private:

};
//在替代之后,派生类对象就可以作为基类的对象使用,但是只能使用从基类继承的成员
void howToprinT(Parent *base)
{
	base->prinT();
}

void howToprinT2(Parent &base)
{
	base.prinT();//基类的成员函数
}
int main()
{
	Parent p1;
	p1.prinT();

	Child c1;
	c1.prinT();
	c1.printC();

	//赋值兼容性原则 
	//1-1 基类指针 (引用) 指向 子类对象
	Parent *p = NULL;
	p = &c1;
	p->prinT();

	//1-2 指针做函数参数
	howToprinT(&p1);
	howToprinT(&c1);//子类对象可以当作父类对象使用

	//1-3引用做函数参数
	howToprinT2(p1);
	howToprinT2(c1);

	//第二层含义

	//可以让子类对象   初始化   父类对象
	//子类就是一种特殊的父类
	Parent p3 = c1;//需要拷贝构造函数
	p3.prinT();
//	总结:子类就是特殊的父类(base *p = &child;)
	system("pause");
	return 0;
}

3.3.2继承中的对象模型

类在C++编译器的内部可以理解为结构体

子类是由父类成员叠加子类新成员得到的

继承中构造和析构

问题:如何初始化父类成员?父类与子类的构造函数有什么关系

         在子类对象构造时,需要调用父类构造函数对其继承得来的成员进行初始化

         在子类对象析构时,需要调用父类析构函数对其继承得来的成员进行清理

3.3.3继承中的构造析构调用原则

1、子类对象在创建时会首先调用父类的构造函数

2、父类构造函数执行结束后,执行子类的构造函数

3、当父类的构造函数有参数时,需要在子类的初始化列表中显示调用

4、析构函数调用的先后顺序与构造函数相反

//demo06_继承中构造和析构.cpp
#include <iostream>
using namespace std;

//观看一个类多个对象中  继承中的构造析构调用原则
//结论
//先 调用父类构造函数 在调用 子类构造函数
//析构的顺序  和构造相反

/*
1、子类对象在创建时会首先调用父类的构造函数
2、父类构造函数执行结束后,执行子类的构造函数
3、当父类的构造函数有参数时,需要在子类的初始化列表中显示调用
4、析构函数调用的先后顺序与构造函数相反
*/

class Parent
{
public:
	Parent()
	{
		this->a = 0;
		this->b = 0;
		cout << "父类的默认构造函数" << endl;
	}
	Parent(int m_a, int m_b)
	{
		this->a = m_a;
		this->b = m_b;
		cout << "父类的有参构造函数" << endl;
	}
	~Parent()
	{
		cout << "父类的析构函数" << endl;
	}
private:
	int a;
	int b;
};

class Child:public Parent
{
public:
	Child(int a, int b, int c) : Parent(a, b)//初始化列表,否则会报错
	{
		this->c = c;
		cout << "子类的构造函数" << endl;
	}

	~Child()
	{
		cout << "子类的析构函数" << endl;
	}

	void printC()
	{
		cout << "我是儿子" << endl;
	}
private:
	int c;
};

void playObj()
{
	Child c1(1, 2, 3);
}

void main()
{
	playObj();

}
/*
父类的有参构造函数
子类的构造函数
子类的析构函数
父类的析构函数
请按任意键继续. . .
*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值