《Effective Modern C++》学习笔记 - Item 12: 使用 override 关键字声明重写函数

  • C++11中引入了 override 关键字,现在当你在派生类中重写基类函数时应添加这一关键字。熟悉 Java 或 C# 的同学应该对此不陌生。本节核心内容就是这句话,接下来关于成员函数性质的讨论可能有些偏题,不过笔者认为其中的信息是非常有价值的。
  • 函数重载(overload)和函数重写(override),二者名称相似但完全无关,这里不再赘述。
  • 以下函数均不会重写,然而也不会有任何报错(甚至连 warning 都没有)。这些都是我们可能无意识会犯的错误。
class Base
{
public:
    virtual void mf1() const 	{ cout << "base class mf1" << '\n'; }
    virtual void mf2(int x) 	{ cout << "base class mf2" << '\n'; }
    virtual void mf3() & 		{ cout << "base class mf3" << '\n'; }
    void mf4() 					{ cout << "base class mf4" << '\n'; }
};

class Derived: public Base
{
public:
    virtual void mf1() 			{ cout << "derived class mf1" << '\n'; }
    virtual void mf2(float x) 	{ cout << "derived class mf2" << '\n'; }
    virtual void mf3() && 		{ cout << "derived class mf3" << '\n'; }
    void mf4() 					{ cout << "derived class mf4" << '\n'; }
};

在这里插入图片描述

  • 添加 override 关键字会引起编译错误进行提示:
    在这里插入图片描述
  • override 关键字仅在特定上下文中生效,因此如果有旧代码声明了如下函数,迁移到C++11不会引起报错和歧义:
class Warning { 		// potential legacy class from C++98
public:
	...
	void override(); 	// legal in both C++98 and C++11
						// (with the same meaning)
};
  • 函数可以被重写的条件:
    1. 基类函数被声明为 virtual (Java中函数默认如此)。
    2. 基类函数和派生类函数名称相同(析构函数除外)。
    3. 基类函数和派生类函数参数类型相同。
    4. 基类函数和派生类函数 const 性质相同(注:即 mf1 后面的 const,代表函数内不能对数据成员进行修改或调用非const函数)。
    5. 基类函数和派生类函数的返回值类型和异常类型必须是适配的。重点返回值类型不一定完全相同(identical),只要是满足基类函数出现的地方派生类函数都能替代即可(如基类指针与派生类指针),这种关系称为 covariance,原文中作者用了意思相近的 compatible 一词。当返回值不适配时VS报错如下,其中“协变”应该就是covariance一词的(晦涩的)翻译。
      在这里插入图片描述
      不相同但适配:
      在这里插入图片描述
    6. C++11添加规则:函数的引用标识符必须相同。 引用标识符的作用是声明成员函数是被作为左值还是右值的主体(*this)被调用时才有效,如下例:
      在这里插入图片描述
  • 当成员函数希望对于主体是左值还是右值的调用进行不同处理时(笔者理解:一般是对右值情况可以进行优化),可以使用引用标识符。例如,上例的 data() 函数要返回类中存储的一个成员 values,类型是 std::vector<double>,数组内的数据量可能很大。假设调用语句为 auto v = w.data();,没有引用标识符时,无论调用者 w 是左值还是右值,v 都会被复制构造。然而我们能发现,当 w 是右值时(例如来自一个工厂函数),该复制纯粹是浪费时间,直接返回 value 的右值版本即可,使 vvector 的移动构造函数初始化。代码如下:
class Widget {
public:
    using DataType = std::vector<double>;

    DataType data() & {
        return values;
    }

    DataType data() && {
        return std::move(values) ;
    }
private:
    DataType values;
};
  • 对以上推论的有效性进行实验:
    在这里插入图片描述

  • values 初始化为10000个元素,循环执行一百万次,使用C++11 chrono 库计时,VS配置设为Release,仅执行不调试,结果如下(执行三次,计毫秒):

    • 不添加右值引用版函数:5758ms, 5990ms, 5686ms
    •  添加右值引用版函数:2209ms, 2107ms, 2025ms
  • 性能提升达到一倍以上,可见如此优化的必要性。

总结

  1. 使用 override 关键字声明重写函数。
  2. 引用标识符允许区分成员函数在左值还是右值对象上被调用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值