C++实验二之继承和多态

一、继承访问权限测试

1.1 设计类A具有public, protected, private等不同属性的成员函数或变量;

class A{
public:
	int m_A;
protected:
	int m_B;
private:
	int m_C;		
};

1.2 类B通过public, protected, private等不同方式继承A,在类B的成员函数中测试访问A的成员函数或变量;

//公共继承
class B1:public A{
public:
	void fun1(){
	
		m_A;//可以访问public 权限为public
		m_B;//可以访问protected  权限为protected
		//m_C;// 不能访问父类的private
		
	}
	
};


//保护继承
class B2:protected A{
public:
	void fun2(){
		m_A;//可以访问public 权限为protected
		m_B;//可以访问protected  权限为protected
		//m_C;//不能访问父类的private
	}
};

//私有继承
class B3:private A{
public:
	void fun3(){
		m_A;//可以访问public 权限为private
		m_B;//可以访问protected 权限为private
		//m_C;//不能访问父类的private
	}
};

综上,公共继承的属性权限不变;保护继承的属性权限到子类只有public变为protected,其他两类不变;私有继承的属性权限到子类全为private。注意一点:父类的私有属性不论被这三种任意一种方式继承,还是私有,故它的继承类是无法访问父类的私有属性的。

1.3 在类B中添加public, protected, private等不同属性的成员函数或变量,在外部测试访问B的各个成员函数或变量;

//新建B类,定义三种成员变量
class B{
public:
	int m_BA;
protected:
	int m_BB;
private:
	int m_BC;
};
//在外部访问B类这三种变量
void test01(){
	B b;
	b.m_BA;//在外部可以访问B类的public成员变量
	//b.m_BB;//在外部不能访问B类的protected成员变量
	//b.m_BC; //在外部不能访问B类的private成员变量
	
}

通过这段测试代码可以得出结论:在外部访问类内属性,只有public作用域下的才能被外部访问;其他两种protected以及private不允许被外部访问。

1.4 B以private方式继承A,尝试把A中的部分public成员提升为public。

//B private继承 A,把A中的public提升为public
class B4:private A{
public:
	using A::m_A;

};
//在外部访问B4
void test02(){
	B4 b4;
	
	//在B4类中,通过私有继承,把A中的所有成员变量都变成了private
	//但是在该类下的public作用域,通过 using A::m_A
	//使得继承下来原本是私有属性的m_A,变成了public
	b4.m_A;//可以在外部访问,因此它的属性是public
}

提升权限的方法,在该作用域下(如:public),使用 using A::m_A 这句关键代码即可。

二、友元类继承测试

2.1 设计类A含有私有变量a,在类A中友元给类C;

class C;//告诉编译器有这么个类,待会儿写
class A{
	friend class C;//C是A的友元,可以访问到A中的私有成员
private:
	int m_A;
};

2.2 设计类B继承A,添加私有变量b;在类C中测试访问类B的成员变量a, b;

class B:public A{
private:
	int m_B;
};
class C{
public:
	 void fun1(){
	 	A a;
	 	a.m_A;//可以访问A的私有成员
	 	
	 	B b;
	 	b.m_A;//C可以访问B从A继承下来的私有成员变量a
	 	//b.m_B;//C不能访问B自己的私有成员b ,即不能访问C的朋友A的儿子的私有成员
	 }

};

得出结论:类作友元可以访问其私有成员,即便它被继承,也可以在子类访问父类的私有;但是友元权限不能被继承,如在这个案例中,B的父亲A是C的友元,然而C是不能访问A的儿子的私有成员的。

2.3 设计类D继承C,在D的成员函数中测试访问类A的成员变量a,类B的成员变量a, b。

class D:public C{
public:
	void fun2(){
		A a;
		//a.m_A; //D访问不到A的私有成员a,即使它的父亲C能访问到
		
		B b;
		//b.m_A; //D访问不到B从A继承下来的私有成员a
		//b.m_B; //D访问不到B自己本身的私有成员b
		
	}
};

综上,类作友元的时候,无法访问对方子类的私有成员,但是可以访问友元自己的私有成员,因为它们之间的关系是friend。
同时,它的儿子也继承不到友元的这一权限 ,即:它的父亲能访问A的私有成员,但是作为父亲的继承,这个子类是无法访问父类的友人A的私有的。
得出结论:友元权限无法被继承,也不能访问别人的子类。

三、多态性综合运用

首先,介绍一下两种多态:
静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
动态多态: 派生类和虚函数实现运行时多态
而他们两者之间的区别是:
静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址
另外,多态要满足以下条件:
1、有继承关系
2、子类重写父类中的虚函数
多态使用:
父类指针或引用指向子类对象

3.1 一般多态性函数:输入输出参数完全一样,在父类中添加virtual;

实际上,这是一种子类对父类的函数进行重写,即:函数返回值类型 ,函数名,参数列表完全一致。
创建父类:动物
创建它的子类:骆驼和羊

class Animal {
public:
	int m_Age=0;
public:
	virtual void speak() {
		cout << "动物在说话" << endl;
	}
};

class Sheep :public Animal{
public:
	void speak() {
		cout << "羊在说话" << endl;
	}
};

class Camel :public Animal{
public:
	void speak() {
		cout << "骆驼在说话" << endl;
	}
};

使用代码进行测试

void doWork(Animal &a) {
	a.speak();
}

int main() {
	Sheep s;
	doWork(s);
	cout << "----------动物分割线------------" << endl;
	Camel c;
	doWork(c);
	return 0;
}

输出如下:
在这里插入图片描述
这是因为父类的函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。

3.2 特殊多态性函数:输入或输出参数在子类中是父类的指针或基类的引用,在子类中对于的是子类的指针或子类的引用;

先在Animal的一个子类Sheep类加个函数

void doSpeak(Animal& a) {
		a.speak();
	}

对此进行测试

void test01() {
	Sheep s;
	Animal a;
	Sheep s1;
	cout<<"子类函数的参数实现多态:" << endl;
	s.doSpeak(a);
	cout << "---------动物分割线--------" << endl;
	s.doSpeak(s1);
}

输出结果如下:
在这里插入图片描述
可以看到,无论是在类内还是类外,多态都是能实现的。

3.3 析构函数的多态性;

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。解决方式:将父类中的析构函数改为虚析构或者纯虚析构。在此我只以虚析构进行举例来分析其多态性。
虚析构:可以解决父类指针释放子类对象以及需要有具体的函数实现。

class Animal {
public:
	int m_Age;
public:
	virtual void speak() {
		cout << "动物在说话" << endl;
	}
	Animal()
	{
		cout << "Animal 构造函数调用!" << endl;
	}
	virtual ~Animal()//加上virtual 变成虚析构,这样子类可以对析构函数重写
	{
		cout << "Animal 析造函数调用!" << endl;
	}
};
class Cat :public Animal {
public:
	string *m_Name;
public:
	Cat(string name) {
		cout << "Cat 构造函数调用!" << endl;
		m_Name = new string(name);
	}
	~Cat() {
		cout << "Cat析构函数调用!" << endl;
		if (this->m_Name != NULL) {
			delete m_Name;
			m_Name = NULL;
		}
	}
	void speak() {
		cout <<*m_Name<< "猫在说话" << endl;
	}

};

进行测试

void test02() {
	Animal* animal = new Cat("Tom");
	animal->speak();
	delete animal;
}

输出结果如下:
在这里插入图片描述

通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄漏。解决方案:给基类增加一个虚析构函数,这样就能通过父类指针释放子类对象。

3.4 多继承,注意什么情况需要虚继承

关于多继承:语法: class 子类 :继承方式 父类1 , 继承方式 父类2…
多继承可能会引发父类中有同名成员出现,需要加作用域区分
关于虚继承:
虚继承应在菱形继承(两个派生类继承同一个基类、又有某个类同时继承者两个派生类,而菱形继承带来的主要问题是子类继承两份相同的数据,导致资源浪费以及毫无意义)的情况下使用
虚继承的目的是让某个类做出声明,承诺愿意共享它的基类。其中,这个被共享的基类就称为虚基类(Virtual Base Class),本例中的 Animal就是一个虚基类。在这种机制下,不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员。

class Animal{
public:
	int m_Age;
};
//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Camel : virtual public Animal {};
class Alpaca : public Sheep, public Camel {};
void test03() {
	Alpaca al;
	cout << "修改Animal的年龄:" << endl;
	al.Animal::m_Age = 150;
	cout << "al.Animal::m_Age = "<< al.Animal::m_Age <<endl;
	cout << "al.Sheep::m_Age = " << al.Sheep::m_Age << endl;
	cout << "al.Camel::m_Age = " << al.Camel::m_Age << endl;
	al.Sheep::m_Age = 100;
	cout << "修改Sheep的年龄:" << endl;
	cout << "al.Animal::m_Age = " << al.Animal::m_Age << endl;
	cout << "al.Sheep::m_Age = " << al.Sheep::m_Age << endl;
	cout << "al.Camel::m_Age = " << al.Camel::m_Age << endl;
	al.Camel::m_Age = 50;
	cout << "修改Camel的年龄:" << endl;
	cout << "al.Animal::m_Age = " << al.Animal::m_Age << endl;
	cout << "al.Sheep::m_Age = " << al.Sheep::m_Age << endl;
	cout << "al.Camel::m_Age = " << al.Camel::m_Age << endl;

}

通过测试可知确实只有一份数据。

3.5 设计矢量图,运用多继承设计组合图形,要求具备创建不同类型矢量图、选择图形、移动图形、用不同颜色显示图形(表示选中与否),用vector或数组管理图形

这里暂且先使用多继承的方式简单设计图形(重点还是为了深入理解多继承而做案例说明)

//默认正三角形
class Triangle {
public:
	int m_di;
	int m_gao;
	int m_color;
	
};
class Square {
public:
	int m_chang;
	int m_kuan;
	int m_color;
};
class Mix :public Triangle, public Square {
public:
	//组合图形继承了三角形以及矩形的属性
	Mix(int chang, int kuan,int c1, int di, int gao,int c2){
		m_chang = chang;
		m_kuan = kuan;
		Square::m_color = c1;//父类属性成员同名记得加作用域
		m_di = di;
		m_gao = gao;
		Triangle::m_color = c2;
	}
	
};

有两个父类:三角形和矩形。它们都被子类Mix继承,即子类是这两个图形的联合体。这样通过输出,就能获得它们不同颜色的联合体了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值