类继承


从一个类派生出另一个类时,原始类称为 基类,继承类称为 派生类派生类对象包含基类对象

公有派生

使用public声明头,基类的公有成员将成为派生类的公有成员;基类的私有部分也将成为派生类的一部分,但是只能通过基类的公有和保护方法来访问。

在继承特性添加:

  • 派生类需要自己的构造函数,给新成员和继承的成员提供数据;
  • 派生类可以根据需要添加额外的数据成员和成员函数;

构造函数:访问权限
派生类的构造函数必须使用基类构造函数。创建派生类对象时,程序首先创建基类对象,C++使用成员初始化列表语法完成这种工作。
有关派生类构造函数:

  • 首先创建基类对象;
  • 派生类构造函数应初始化派生类新增的数据成员;
  • 派生类构造函数应通过成员列表将基类信息传递给基类构造函数。

派生类对象过期时,程序将首先调用派生类析构函数,再调用基类析构函数。

派生类和基类的关系

  • 派生类对象可以使用基类的非私有方法。
  • 基类指针可以在不进行显示类型转换的情况下指向派生类对象。
  • 基类引用可以在不进行显示类型转换的情况下引用派生类对象。

注意:

  1. 然而基类指针或者引用只能用于调用基类方法,不能调用派生类的方法。不可以将基类对象和地址赋给派生类引用和指针。
  2. 可以将基类对象初始化为派生类对象,也可以将派生对象赋给基类对象。

多态公有继承

多态:同一个方法的行为随上下文而异。

实现多态公有继承的两种机制:

  • 在派生类中重新定义基类的方法。
  • 在基类使用虚方法,用关键字virtual,使得该方法在基类以及所有的派生类中是虚的。

方法是通过对象调用的,它将根据对象的类型确定使用哪一种方法;方法是通过引用或指针调用的,将分有没有使用关键字virtual:

  • 如果没有使用关键字,程序将根据引用类型或指针类型选择方法;
  • 如果使用关键字,程序将根据引用或指针指向的对象的类型来选择方法

注意:非构造函数不能使用成员初始化列表语法,但派生类方法可以调用公有的基类方法,必须利用作用域解析运算符。

虚析构函数:如果析构函数不是虚的,则只调用对应于指针类型的析构函数。如果析构函数是虚的,将调用相应对象类型的析构函数。

静态联编和动态联编

编译器对非虚方法使用静态联编;对虚方法使用动态联编。
向上强制转换:将派生类引用或指针转换为基类引用或指针。
向下强制转换:将基类指针或引用转换为派生类指针或引用。

虚函数工作原理

编译器为每个对象添加一个隐藏成员,隐藏成员包含一个指向函数地址数组的指针,这个数组称为虚函数表。虚函数表中存储了为类对象进行声明的虚函数地址,即该类的所有虚函数地址。例如,基类对象包含一个指针,指向基类中所有虚函数地址,派生类对象包含一个指向独立地址表的指针,派生类的虚函数表会保存虚函数新定义的地址新的虚函数定义地址、以及没有重新定义的虚函数地址

虚函数成本:

  • 每个对象都将增大,增大量为存储地址的空间;
  • 对于每个类,编译器都创建一个虚函数地址表(数组);
  • 对于每个函数调用,都需要执行一次额外的操作,即到表中查找地址;

虚函数注意事项

  1. 构造函数不能是虚函数。创建派生类对象时,将调用派生类的构造函数,然后,派生类的构造函数将使用基类的一个构造函数。因此,派生类不继承基类的构造函数。
  2. 析构函数必须是虚函数,除非类不用作基类,通常应该给基类提供一个虚析构函数,即使它不执行任何操作。
  3. 友元不能是虚函数,因为只有成员才能是虚函数。
  4. 重新定义将隐藏方法,不会生成函数的两个重载版本。如果在派生类中重新定义函数,则隐藏同名的基类方法,当特征标不同可能会出现编译器警告。有两条经验规则:
    1. 如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针,只适用于返回值。
    2. 如果基类声明被重载了,则应在派生类中重新定义所有的基类版本,如果只重新定义一个版本,则另外两个版本将被隐藏,派生类对象无法使用它们。如果不需要修改,则新定义可只调用基类版本。

访问控制:protect

在类外,只能用公有类成员来访问protect部分的成员。派生类的成员可以直接访问基类的保护成员,但不能直接访问基类的私有成员。

抽象基类

从不同的类中抽象出共性,放到ABC中。C++通过纯虚函数提供未实现的函数,纯虚函数声明的结尾处为=0。

当类中包含纯虚函数时,不能创建该类的对象,包含纯虚函数的类只能用作基类。抽象基类必须至少包含一个纯虚函数。当所有的基类方法都可以定义,但是仍需要将这个类声明为抽象的,可以将其中一个原型声明为虚的,但是可以在实现文件中提供方法的定义。

继承和动态内存分配

如果基类使用动态内存分配,并重新定义赋值和复制构造函数,派生类的实现它的属性有关。(P516)

派生类不使用new

不需要为派生类定义显示析构函数、复制构造函数和赋值运算符。

派生类使用new

必须为派生类定义显示析构函数、复制构造函数和赋值运算符。
对于派生类中的析构函数,释放自己的内存,随后会自动调用基类的析构函数。
对于派生类中复制构造函数,通过初始化列表调用基类的复制构造函数完成派生类中基类部分的数据。
对于派生类中的赋值运算符,通过使用作用域解析运算符显示地调用基类的赋值运算符来完成,再完成自己的赋值。

类设计回顾

默认构造函数

自动生成的默认构造函数可以调用基类的默认构造函数以及调用本身是对象的成员所属的默认构造函数。如果派生类构造函数的成员初始化列表中没有显示调用基类构造函数,则编译器将使用基类的默认构造函数来构造派生类对象的基类部分,这里基类的默认构造函数必须是自己定义的,编译器此时因为有了构造函数,不会提供默认构造函数。

构造函数

构造函数不同于其他类方法,因为它创建新的对象,而其他的方法只是被现有的对象调用,所以构造函数不被继承。因为继承意味着派生类对象可以使用基类的方法,而构造函数完成其任务前不会有对象。

复制构造函数

复制构造函数接受其所属类的对象作为参数。
在下述情况使用复制构造函数:

  • 将新对象初始化为一个同类对象;
  • 按值将对象传递给函数;
  • 函数按值返回对象;
  • 编译器生成临时对象;

赋值运算符

如果语句创建新的对象,则使用初始化;如果语句修改已有对象的值,则是赋值。如果需要显示定义复制构造函数,则基于相同的原因,也需要显示定义赋值运算符。

析构函数

一定要定义显示析构函数来释放类构造函数使用new分配内存,对于基类应该提供一个虚析构函数。

转换

使用一个参数就可以调用的构造函数定义了从参数类型到类类型的转化。在带一个参数的构造函数原型中使用explicit将禁止进行隐式转换,但仍允许显式转换。
转换函数:operator typeName(); 必须是类方法。

按值传递对象与传递引用

按值传递对象涉及到生成临时拷贝,即调用复制构造函数,然后调用析构函数,调用这些函数需要时间。

返回对象和返回引用

如果函数返回在函数中创建的临时对象,则不要使用引用。如果函数返回的是通过引用或指针传递给它当做参数的对象,应按引用返回对象。

使用const

可以确保方法不修改参数,不修改调用它的对象。

公有继承的考虑因素

  1. 表示is-a关系的方式之一是,无需进行显示类型转换,基类指针就可以指向派生类对象,基类引用可引用派生类对象,反之不行。
  2. 构造函数和析构函数都不能被继承,在释放程序时,程序先调用派生类的析构函数,然后调用基类的析构函数。如果基类有默认析构函数,编译器将为派生类生成默认析构函数。(基类自己定义了默认析构函数,不是编译器生成的,编译器才会为派生类生成)
  3. 赋值运算符不能被继承的。如果类构造函数使用new来初始化指针,则需要提供一个显示赋值运算符(因为有指针,不能是成员赋值,来避免代码错误),同时在派生类中,因为使用基类的赋值运算符为派生对象的基类部分赋值,则不需要为派生类重新定义赋值运算符。如果派生类使用new,则必须提供显示赋值运算符,必须给类的每个成员提供赋值运算符。
hasDMA & hasDMA :: operator=(const hasDMA & hs)
{
	if(this==&hs)
		return *this;
	baseDMA :: operator=(hs);//赋值基类部分
	delete [] style;
	style = new char[std::strlen(hs.style)+1];
	std::strcpy(style,hs.style);//赋值新成员
	return *this;
}

以公有方式派生的类的对象可以通过多种方式使用基类方法

  • 派生类对象自动使用继承而来的基类方法,如果派生类没有重新定义该方法。
  • 派生类的构造函数自动调用基类的构造函数。
  • 派生类方法可以使用作用域解析运算符来调用公有的和受保护的基类方法。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值