当前位置: 首页 > news >正文

C++学习笔记----9、发现继承的技巧(六)---- 有趣且令人迷惑的继承问题(6)

4、继承类中的拷贝构造函数与同仁操作符

        上一章解释了当在类中有动态分配的内存时必须提供拷贝构造函数与赋值操作符。当定义继承类时,需要小心对待拷贝构造函数与operator=。

        如果继承类没有特殊数据(通常是指针)需要非缺省拷贝构造函数或operator=,不需要硬来一个,而不管基类是否有一个。如果继承类省略了拷贝构造函数或operator=,在继承类中的数据成员会被提供缺省的拷贝构造函数或operator=,基类拷贝构造函数或operator=会被用于基类中指定的数据成员。

        另一方面,如果在继承类中确实指定了拷贝构造函数,需要显式调用父类的拷贝构造函数,如下代码所示。如果不这样做,缺省构造函数(不是拷贝构造函数)就会用于对象的父类部分。

class Base
{
public:virtual ~Base() = default;Base() = default;Base(const Base& src) { }
};class Derived : public Base
{
public:Derived() = default;Derived(const Derived& src) : Base{ src } { }
};

        同样的,如果继承类重载了operator=,差不多也总是需要调用父类版本的operator=。唯一不需要这么做的情况是如果有某些奇怪的原因,只想当赋值发生的时候赋值部分对象。下面的代码展示了如何 在继承类中调用父类的赋值操作符:

Derived& operator=(const Derived& rhs)
{if (&rhs == this) {return *this;}Base::operator=(rhs); // Calls parent's operator=.// Do necessary assignments for derived class.return *this;
}

        警告:如果继承类没有指定它的拷贝构造函数或operator=,基类功能继续工作。然而,如果继承类确实提供了自己的拷贝构造函数或operator=,它需要显式地调用基类版本。

        注意:当需要在继承层次结构中的拷贝功能时,专业c++开发者通用的方法是实现多态clone()成员函数,因为单纯依赖标准拷贝构造函数与拷贝赋值操作符是不够的。多态clone()会在以后的章节中讨论。

5、运行时类型装备

        与其它面向对象编程语言相关,c++是面向编译时的。前面已经学到,重载成员函数好用的原因是在成员函数与其实现之间是间接层次,而不是对象有内建的自身类的知识。

        然而,c++的属性提供了对象的运行时观点。这些属性通常以叫做运行时类型信息(RTTI)属性集来分类。RTTI提供了一些有用的属性来工作于对象的类成员。dynamic_cast就是这样的一个属性,它允许在面向对象的层次结构中的类型的安全转换;这在本章前面也讨论过了。在没有vtable的类上使用dynamic_cast(),也就是说,没有任何virtual成员函数,会造成编译错误。

        第二个RTTI属性是typeid操作符,它让你在运行时查询类型。应用这个操作符的结果是一个指向std::type_info对象的引用,在<typeinfo>中定义。type_info类有一个成员函数叫做name()返回类型的编译依赖的名字。typeid操作符行为如下:

  • typeid(type):结果是指向type_info的引用,表示给定的类型。
  • typeid(expression)
  • 如果检测expression的结果是一个多态类型,那么 expression被检测,typeid操作符的结果是一个指向type_info对象的xhet ,表示检测过的expression的动态类型。
  • 否则,expression不被检测,结果是指向type_info对象的引用,表示静态类型。

        在大部分情况下,不需要使用typeid,因为任何代码基于对象类型有条件执行都会更好地处理,例如,virtual成员函数。

        下面的代码使用typeid打印基于对象类型的信息:

class Animal { public: virtual ~Animal() = default; };
class Dog : public Animal {};
class Bird : public Animal {};void speak(const Animal& animal)
{if (typeid(animal) == typeid(Dog)) {println("Woof!");} else if (typeid(animal) == typeid(Bird)) {println("Chirp!");}
}

        当你看到这样的代码,马上考虑使用virtual成员函数来重新实现其功能。在这种情况下,更好的实现是在Animal基类中声明一个叫做speak()的virtual成员函数。Dog会重载成员函数打印“Woof”,而Bird会重载它打印“Chirp!”。这个方法更符合面向对象编程,与对象相关的功能给到那些对象。

        警告:只有在类有至少一个virtual成员函数时typeid操作符才会正确工作,也就是说,当类有一个vtable。还有,typeid操作符剥离了参数的引用与const指示符。

        typeid操作符的一个可能的使用场景是日志与查错目的。下面的代码使用typeid来做日志。logObject()函数用”loggable”对象作为参数。设计是任何从Loggable类继承的对象都可以被做日志,并且 支持叫做getLogMessage()的成员函数。

class Loggable
{
public:virtual ~Loggable() = default;virtual string getLogMessage() const = 0;
};class Foo : public Loggable
{
public:string getLogMessage() const override { return "Hello logger."; }
};void logObject(const Loggable& loggableObject)
{print("{}: ", typeid(loggableObject).name());println("{}", loggableObject.getLogMessage());
}

        logObject()首先打印对象类的名字到控制台,后面跟着日志信息。用这种方式,当你以后读日志时,可以看到哪个对象负责哪一行。下面是微软Visual C++2022生成的输出,当logObject()用Foo实例调用时:

class Foo: Hello logger.

        可以看到,typeid操作符返回的名字是“class Foo”。然而,这个名字由编译器决定。例如,如果用GCC编译并且运行同样的代码,输出就是下面这样:

3Foo: Hello logger.

        注意:如果不是出于日志与排错的目的使用typeid,考虑改变设计,例如,使用virtual成员函数。


http://www.mrgr.cn/news/62320.html

相关文章:

  • 观察者模式和订阅发布模式的关系
  • openmv运行时突然中断并且没断联只是跟复位了一样
  • LosslessScaling-学习版[steam价值30元的游戏无损放大/补帧工具]
  • Calibre(阅读转换)-官方开源中文版[完整的电子图书馆系统,包括图书馆管理,格式转换,新闻,材料转换为电子书]
  • 双目立体校正和Q矩阵
  • python实现pdf转word和excel
  • <HarmonyOS第一课>给应用添加通知和提醒的习题
  • VC2012创建弹出式菜单
  • 智能进阶之路:从基础模型到个性化代理—探索Agent与微调的共生之道
  • [专有网络VPC]创建和管理流量镜像
  • 神奇的数据恢复工具:让丢失的数据重现
  • 线上 Dump
  • 【数据结构】链表详解:数据节点的链接原理
  • 积鼎国产CFD软件VirtualFlow新版上线:新增30余项新功能,多相流仿真效率升级
  • C#与C++交互开发系列(十七):线程安全
  • MyBatis-Plus:简化 CRUD 操作的艺术
  • 「动态规划」1/n:什么是动态规划?
  • 能通过Ping命令访问CentOS 9 Stream,但在使用Xshell连接
  • SQLI LABS | Less-20 POST-Cookie Injections-Uagent field-error based
  • Python酷库之旅-第三方库Pandas(178)
  • MySQL Workbench Data Import Wizard:list index out of range
  • Robot Framework 搭建环境
  • C# 编程语言学习教程
  • vuex、vue-router实现原理
  • AcWing 1303:斐波那契前 n 项和 ← 矩阵快速幂加速递推
  • 生成树协议——STP/RSTP/MSTP