C++基础教程面向对象(学习笔记(55))

override和final说明符,以及协变返回类型

为了解决继承的一些常见挑战,C ++ 11为C ++添加了两个特殊标识符:override和final。请注意,这些标识符不被视为关键字 - 它们是在某些上下文中具有特殊含义的普通标识符。

虽然final不是很常用,但是override是一个非常棒的补充,你应该使用。在本课中,我们将看一下虚拟函数override返回类型必须匹配的规则的一个例外。

override说明符

正如我们在上一课中提到的,如果派生类虚函数的签名和返回类型完全匹配,则它只被视为重写。这可能导致无意的问题,其中一个旨在实现覆盖的功能实际上不是。

请考虑以下示例:

class A
{
public:
	virtual const char* getName1(int x) { return "A"; }
	virtual const char* getName2(int x) { return "A"; }
};
 
class B : public A
{
public:
	virtual const char* getName1(short int x) { return "B"; } // 注意:参数是一个短整数
	virtual const char* getName2(int x) const { return "B"; } // 注意:函数是const
};
 
int main()
{
	B b;
	A &rBase = b;
	std::cout << rBase.getName1(1) << '\n';
	std::cout << rBase.getName2(2) << '\n';
 
	return 0;
}

因为rBase是对B对象的A引用,所以这里的意图是使用虚函数来访问B :: getName1()和B :: getName2()。但是,因为B :: getName1()采用不同的参数(short int而不是int),所以它不被视为A :: getName1()的重写。更隐蔽的是,因为B :: getName2()是const而A :: getName2()不是,所以B :: getName2()不被认为是A :: getName2()的重写。

因此,该程序打印:

A
A

在这种特殊情况下,因为A和B只是打印它们的名字,所以很容易看出我们搞砸了我们的override,并且调用了错误的虚函数。但是,在一个更复杂的程序中,函数具有行为或返回未打印的值,这些问题可能很难调试。

为了帮助解决那些意图override但不是override的函数的问题,C ++ 11引入了覆盖说明符。通过将说明符放在const所在的相同位置,可以将override应用于任何override函数。如果函数未override基类函数,则编译器会将该函数标记为错误。

class A
{
public:
	virtual const char* getName1(int x) { return "A"; }
	virtual const char* getName2(int x) { return "A"; }
	virtual const char* getName3(int x) { return "A"; }
};
 
class B : public A
{
public:
	virtual const char* getName1(short int x) override { return "B"; } // 编译错误,函数不是覆盖
	virtual const char* getName2(int x) const override { return "B"; } // 编译错误,函数不是覆盖
	virtual const char* getName3(int x) override { return "B"; } // 可以,函数是A :: getName3(int)的重写
 
};
 
int main()
{
	return 0;
}

上面的程序产生两个编译错误:一个用于B :: getName1(),另一个用于B :: getName2(),因为它们都不会override先前的函数。B :: getName3()会override A :: getName3(),因此不会为该行生成错误。

使用override说明符没有性能损失,它有助于避免意外错误。因此,我们强烈建议您将其用于您编写的每个虚函数override,以确保您实际上override了您认为的函数。

规则:将override说明符应用于您编写的每个预期override函数。

final说明符

在某些情况下,您可能不希望某人能够override虚拟函数或从类继承。最终说明符可用于告诉编译器强制执行此操作。如果用户尝试覆盖已指定为final的函数或类,则编译器将给出编译错误。

在我们想要限制用户覆盖函数的情况下,最后的说明符在override指定符的相同位置使用,如下所示:

class A
{
public:
	virtual const char* getName() { return "A"; }
};
 
class B : public A
{
public:
	// 注意在下一行使用最终说明符 - 这使得该函数不再可覆盖
	virtual const char* getName() override final { return "B"; } // 可以,覆盖A :: getName()
};
 
class C : public B
{
public:
	virtual const char* getName() override { return "C"; } // 编译错误:覆盖B :: getName(),这是最终的
};

在上面的代码中,B :: getName()重写了A :: getName(),这很好。但是B :: getName()具有最终说明符,这意味着该函数的任何进一步覆盖都应被视为错误。事实上,C :: getName()尝试覆盖B :: getName()(此处与final说明符不相关,它只是用于良好实践),因此编译器将给出编译错误。

在我们想要阻止从类继承的情况下,在类名后面应用final说明符:

class A
{
public:
	virtual const char* getName() { return "A"; }
};
 
class B final : public A // 注意:在这里使用最终说明符
{
public:
	virtual const char* getName() override { return "B"; }
};
 
class C : public B // 编译错误:无法从最终类继承
{
public:
	virtual const char* getName() override { return "C"; }
};

在上面的例子中,B类被声明为final。因此,当C尝试从B继承时,编译器将给出编译错误。

协变返回类型

有一种特殊情况,派生类虚函数override可以具有与基类不同的返回类型,仍然被视为匹配覆盖。如果虚函数的返回类型是指针或对类的引用,则override函数可以返回指针或对派生类的引用。这些被称为协变返回类型。这是一个例子:

#include <iostream>
 
class Base
{
public:
	// 此版本的getThis()返回指向Base类的指针
	virtual Base* getThis() { std::cout << "called Base::getThis()\n"; return this; }
	void printType() { std::cout << "returned a Base\n"; }
};
 
class Derived : public Base
{
public:
	// 通常,覆盖函数必须返回与基函数相同类型的对象
	// 但是,因为Derived是从Base派生的,所以可以返回Derived *而不是Base *
	virtual Derived* getThis() { std::cout << "called Derived::getThis()\n";  return this; }
	void printType() { std::cout << "returned a Derived\n"; }
};
 
int main()
{
	Derived d;
	Base *b = &d;
	d.getThis()->printType(); // 调用Derived :: getThis(),返回Derived *,调用Derived :: printType
	b->getThis()->printType(); //调用Derived :: getThis(),返回Base *,调用Base :: printType
}

这打印:

called Derived::getThis()
returned a Derived
called Derived::getThis()
returned a Base

请注意,某些较旧的编译器(例如Visual Studio 6)不支持协变返回类型。

关于协变返回类型的一个有趣的注意事项:C ++无法动态选择类型,因此您将始终获得与被调用函数的基本版本匹配的类型。

在上面的例子中,我们首先调用d.getThis()。由于d是Derived,因此调用Derived :: getThis(),它返回Derived *。然后,此Derived *用于调用非虚函数Derived :: printType()。
现在有趣的案例。然后我们调用b-> getThis()。变量b是指向派生对象的Base指针。Base :: getThis()是虚函数,因此调用Derived :: getThis()。虽然Derived :: getThis()返回Derived *,但由于函数的基本版本返回Base *,返回的Derived *将向下转换为Base *。因此,调用Base :: printType()。

换句话说,在上面的示例中,如果您使用首先键入为Derived对象的对象调用getThis(),则只能获得Derived *。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
**数字乡村解决方案摘要** **国家战略与乡村振兴** 中国正实施国家大数据战略,以加快数字中国的建设,整合数据资源,保障数据安全,并推动产业升级。2023年中央一号文件强调了乡村振兴战略,旨在通过产业兴旺、生态宜居、乡风文明、治理有效和生活富裕五大方面,实现农业农村现代化。 **数字乡村建设的重要性** 数字乡村建设是乡村振兴战略的重要组成部分,通过整合资源数据,推动物联网和遥感技术在农业中的应用,促进农业现代化。它被视为促进乡村转型发展的新方向,是数字化在农业农村经济社会发展中的应用。 **关键要素与效益** 数字乡村建设的关键要素包括数据资源整合、产业促进、农业发展规模化和标准化,以及改善乡村生活服务体系。预期效益包括提升国家治理能力,实现政府决策科学化和社会治理精准化,同时推动公共服务高效化。 **体系架构与数据融合** 数字乡村的体系架构包括乡村生态、基层治理、慧治、慧享、慧融、慧美、慧智和慧图等多个方面,实现城乡、政企、农户各级平台及服务的融合。数据融合是核心,整合乡村人口、产值、环境监测等多方面数据,为乡村治理和产业发展提供支撑。 **多业务协同与智慧治理** 数字乡村通过多业务协同应用平台,实现乡村物联网的布局和触达,涵盖农业生产、农资厂商、消费者等环节。区块链技术在农产品质量溯源中的应用,确保了产品全过程可追溯。乡村智慧治理通过村务管理、财务管理等方面提升治理效能,实现绿色发展理念。综合服务体系通过“互联网+服务”模式,提供便民服务和乡村经济发展支持,推动乡村全面振兴。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值