C++继承和多态

目录

继承

继承的意义

访问限定符、继承方式

赋值兼容规则(切片)

子类的默认成员函数

多继承

继承is a和组合has a

多态

什么是多态

形成多态的条件

函数重载,隐藏,重写的区别

override和final

多态原理


继承

继承的意义

继承本质就是类之间代码的复用,通过子类继承父类的方式,让子类对象中具有父类对象的成员。

访问限定符、继承方式

通过访问限定符,可以限制子类可以继承哪些变量,不能继承哪些变量,在实际开发过程中通常使用公有public继承。父类中想要让子类继承的成员就用protected或public修饰,不想让子类看到就用private修饰。

赋值兼容规则(切片)

子类对象,指针,引用分别赋值给父类对象,指针,引用时,会遵循赋值兼容规则(切片)。即把子类对象中父类对象的可见范围给到父类对象、指针、引用

子类的默认成员函数

默认成员函数主要包括构造函数,拷贝构造,赋值运算符重载,析构函数。那么在子类中他们是如何调用的呢?(考虑到要初始化子类对象中的父类对象

子类的初始化包括对子类成员的初始化和对继承的父类成员的初始化

由于父类的默认成员函数在子类内是可见的,所以在初始化父类成员时,只需要调用父类的默认成员函数即可。子类成员再另当初始化。

例如,基类为Person,子类为Student的代码:

class Person
{
public:
	Person(string name,int age,string gender)
		:_name(name),_age(age),_gender(gender)
	{
		cout << "Person()" << endl;
	}
	Person(const Person& p)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)
	{
		cout << "Person.operator=()" << endl;
		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name;//姓名
	int _age;//年龄
	string _gender;//性别
};
class Student:public Person
{
private:
	string _id;//学号
	string _college;//所属学院
public:
	Student(string name, int age, string gender,string id,string college)
		:Person(name,age,gender)//调用父类构造
		,_id(id),_college(college)
	{
		//初始化子类成员
		cout << "Student()" << endl;
	}
	Student(const Student& s)
		:Person(s)//调用父类拷贝构造
	{
		//拷贝子类成员
		cout << "Student(const Student& s)" << endl;
	}
	Student& operator=(const Student& s)
	{
		Person::operator=(s);//调用父类的operator=
		cout << "Student.operator=()" << endl;
		//赋值子类成员
		return *this;
	}
	~Student()
	{
		//默认自动先调用子类析构,再调用父类析构
		cout << "~Student()" << endl;
	}
};
int main()
{
	Student s("cx", 20, "男", "2022112102601", "计算机与信息工程学院");//构造
	Student s2 = s;//拷贝构造
	Student s3("xxx", 18, "女", "2022112102728", "经济管理学院");//构造
	s3 = s2;//赋值
	return 0;
}

代码输出结果:

多继承

多继承:多继承就是一个类可以继承多个父类。

多继承会导致的问题:菱形继承,图示如下

为什么说菱形继承会有问题?

由于继承的本质是代码的复用,而菱形继承会导致A类对象在D类对象中存在两份,导致数据冗余和二义性。

如何解决:虚继承

虚继承就是多个类 在继承重复类时,都使用虚继承。这样在上图中D对象内就只会有一份A对象,消除了对象的冗余。

继承is a和组合has a

继承和组合本质都是代码的复用,使得一个对象存在于另一个对象之中。

继承:

class B
{
protected:
	int _b;
};
class A :public B
{
//A中可以直接使用父类的protected和public成员
protected:
	int _a;
};

组合:

class B
{
protected:
	int _b;
};
class A
{
protected:
	B _b;//B类对象作为成员,只能使用B类对象的public成员
	int _a;
};

由于组合只能使用另一个对象的公有成员,这使得组合而成的类之间耦合度更低,因此在组合和继承都可以达到目的时,组合方案更优。

多态

什么是多态

多态:多态就是一个接口多种实现,多种状态,是面向对象编程语言的一重要特征。当使用父类指针或引用接收子类对象指针或引用,并调用子类对象中重写的虚函数时,就会体现出多态性。即指向父类调父类,指向子类调子类

class Animal //基类
{
public:
	virtual void say()//虚函数
	{
		cout << "animal say " << endl;
	}
};
class Cat:public Animal  //子类,重写基类的虚函数
{
public:
	virtual void say()
	{
		cout << "cat say miao~miao~" << endl;
	}
};
class Dog :public Animal  //子类,重写基类的虚函数
{
public:
	virtual void say()
	{
		cout << "dog say wang~wang~" << endl;
	}
};
void func(Animal* n)//基类指针/引用接收
{
	n->say();//多态调用
}
int main()
{
	Animal a;
	Cat c ;
	Dog d;
	func(&a);
	func(&c);
	func(&d);
	return 0;
}

上面的代码基类为Animal,子类有Cat和Dog,当使用基类指针或引用接收对象的指针或引用时,指向哪个类调用哪个类的虚函数。

形成多态的条件

1.具有继承关系

2.虚函数重写

3.父类指针或引用调用虚函数

函数重载,隐藏,重写的区别

重载:同一作用域内,函数名相同,参数不同。

隐藏(重定义):子类函数和父类函数名字相同。父类函数就被隐藏了。

重写(覆盖):子类函数和父类函数名字相同,参数相同,返回类型相同,且父类函数是虚函数。

override和final

子类重写父类虚函数时,用override修饰子类重写的函数,用于检查是否成功重写(三同或者协变)。如果不构成重写在编译时就会报错。

用final修饰的类,不允许有子类继承

用final修饰的虚成员函数,在子类中不可被重写

多态原理

首先看一下这段代码

class A
{
public:
	void func()
	{
		;
	}
};
int main()
{
    A a;
	cout << sizeof(a) << endl;
	return 0;
}

因为类的成员函数存放在代码段,并不会在每个对象中单独存放一份,所以这里是空类,但是打印的是1,是因为要用至少一个字节来标识不同的对象。

class A
{
public:
	virtual void func()
	{
		;
	}
};
int main()
{
	A a;
	cout << sizeof(a) << endl;
	return 0;
}

这个类的大小是多少呢?

这又是为什么呢?为什么函数由普通函数变为虚函数,对象的大小就变了呢?

这是因为类中的每个虚函数地址要放到一个专门的位置,即虚函数表中。

对象中会多存放一个虚函数表指针,虚函数表中存放着一个类中所有的虚函数指针。

指针的大小在32位计算机上为4字节,64位计算机为8字节,所以类的大小变为8字节。

这和多态有什么关系呢?

子类继承父类时,会把父类对象中的虚函数表也继承下来。


重写虚函数前:

子类虚函数表中的虚函数地址和父类虚函数表中的虚函数地址是相同的。

重写之后:子类虚函数表中的虚函数地址会被重写的虚函数地址"覆盖"。

虚函数指针被覆盖后,此后在多态的条件下,基类指针指向父类对象时,编译器会在父类对象的虚函数表中查找虚函数,指向子类对象时,编译器会在子类对象的虚函数表中查找虚函数。进而体现出了指向谁,调用谁的多态性。

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 11
    评论
C++中的继承多态和虚函数是面向对象编程的重要概念。 继承是指一个类可以从另一个类继承属性和方法。子类可以继承父类的公有成员和保护成员,但不能继承私有成员。通过继承,子类可以重用父类的代码,并且可以添加自己的特定功能。继承可以实现代码的重用和层次化的设计。 多态是指同一个函数可以根据不同的对象调用不同的实现。多态可以通过虚函数来实现。虚函数是在基类中声明为虚拟的函数,它可以在派生类中被重写。当通过基类指针或引用调用虚函数时,实际调用的是派生类中的实现。这样可以实现动态绑定,即在运行时确定调用的函数。 虚函数的原理是通过虚函数表来实现的。每个包含虚函数的类都有一个虚函数表,其中存储了虚函数的地址。当调用虚函数时,编译器会根据对象的类型在虚函数表中查找对应的函数地址并调用。 综上所述,C++中的继承多态和虚函数是实现面向对象编程的重要机制,它们可以提高代码的灵活性和可扩展性。 #### 引用[.reference_title] - *1* *3* [C++多态之 虚函数和虚函数表](https://blog.csdn.net/weixin_46053588/article/details/121231465)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [c++多态及虚函数表内部原理实战详解](https://blog.csdn.net/bitcarmanlee/article/details/124830241)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

无极太族

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值