item 12: updating

This commit is contained in:
racaljk 2018-06-08 17:58:07 +08:00
parent 7cbcc69900
commit c88c4f06f6

View File

@ -50,11 +50,111 @@ makeWidget().doWork(); // 调用被右值引用限定修饰的Widget::doWork版
```
后面我还会提到引用限定符修饰成员函数,但是现在,只需要记住如果基类的虚函数有引用限定符,派生类的重写就必须具有相同的引用限定符。如果没有,那么新声明的函数还是属于派生类,但是不会重写父类的任何函数。
All these requirements for overriding mean that small mistakes can make a big difference.
Code containing overriding errors is typically valid, but its meaning isnt what
you intended. You therefore cant rely on compilers notifying you if you do something
wrong. For example, the following code is completely legal and, at first sight,
looks reasonable, but it contains no virtual function overrides—not a single derived
class function that is tied to a base class function. Can you identify the problem in
each case, i.e., why each derived class function doesnt override the base class function
with the same name?
这么多的重写需求意味着哪怕一个小小的错误也会造成巨大的不同。
代码中包含重写错误通常是有效的但它的意图不是你想要的。因此你不能指望当你犯错时编译器能通知你。比如下面的代码是完全合法的咋一看还很有道理但是它包含了非虚函数重写。你能识别每个case的错误吗换句话说为什么派生类函数没有重写同名基类函数
```cpp
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
void mf4() const;
};
class Derived: public Base {
public:
virtual void mf1();
virtual void mf2(unsigned int x);
virtual void mf3() &&;
void mf4() const;
};
```
需要一点帮助吗?
+ `mf1`在基类声明为`const`,但是派生类没有这个常量限定符
+ `mf2`在基类声明为接受一个`int`参数,但是在派生类声明为接受`unsigned int`参数
+ `mf3`在基类声明为左值引用限定,但是在派生类声明为右值引用限定
+ `mf4`在基类没有声明为虚函数
你可能会想“哎呀实际操作的时候这些warnings都能被编译器探测到所以我不需要担心。”可能你说的对也可能不对。就我目前检查的两款编译器来说这些代码编译时没有任何warnings即使我开启了输出所有warnings其他编译器可能会为这些问题的部分输出warnings但不是全部
由于正确声明派生类的重写函数很重要但很容易出错C++11提供一个方法让你可以显式的将派生类函数指定为应该是基类重写版本将它声明为`override`。还是上面那个例子,我们可以这样做:
```cpp
class Derived: public Base {
public:
virtual void mf1() override;
virtual void mf2(unsigned int x) override;
virtual void mf3() && override;
virtual void mf4() const override;
};
```
代码不能编译,当然了,因为这样写的时候,编译器会抱怨所有与重写有关的问题。这也是你想要的,以及为什么要在所有重写函数后面加上`override`。使用`override`的代码编译时看起来就像这样(假设我们的目的是重写基类的所有函数):
```cpp
class Base {
public:
virtual void mf1() const;
virtual void mf2(int x);
virtual void mf3() &;
virtual void mf4() const;
};
class Derived: public Base {
public:
virtual void mf1() const override;
virtual void mf2(int x) override;
virtual void mf3() & override;
void mf4() const override; // 可以添加virtual但不是必要
};
```
Note that in this example, part of getting things to work involves declaring mf4 virtual
in Base. Most overriding-related errors occur in derived classes, but its possible
for things to be incorrect in base classes, too.
A policy of using override on all your derived class overrides can do more than just
enable compilers to tell you when would-be overrides arent overriding anything. It
can also help you gauge the ramifications if youre contemplating changing the signature
of a virtual function in a base class. If derived classes use override everywhere,
you can just change the signature, recompile your system, see how much damage
youve caused (i.e., how many derived classes fail to compile), then decide whether
the signature change is worth the trouble. Without override, youd have to hope you
have comprehensive unit tests in place, because, as weve seen, derived class virtuals that are supposed to override base class functions, but dont, need not elicit compiler
diagnostics.
C++ has always had keywords, but C++11 introduces two contextual keywords, over
ride and final.2 These keywords have the characteristic that they are reserved, but
only in certain contexts. In the case of override, it has a reserved meaning only
when it occurs at the end of a member function declaration. That means that if you
have legacy code that already uses the name override, you dont need to change it
for C++11:
```cpp
class Warning { // potential legacy class from C++98
public:
void override(); // legal in both C++98 and C++11
};
```
Thats all there is to say about override, but its not all there is to say about member
function reference qualifiers. I promised Id provide more information on them later,
and now its later.
If we want to write a function that accepts only lvalue arguments, we declare a nonconst
lvalue reference parameter:
```cpp
void doSomething(Widget& w); // accept
If we want to write a function that accepts only rvalue arguments, we declare an
rvalue reference parameter:
```cpp
void doSomething(Widget&& w); // accepts only rvalue Widgets
```
Member function reference qualifiers simply make it possible to draw the same distinction
for the object on which a member function is invoked, i.e., *this. Its precisely
analogous to the const at the end of a member function declaration, which
indicates that the object on which the member function is invoked (i.e., *this) is
const.
The need for reference-qualified member functions is not common, but it can arise.
For example, suppose our Widget class has a std::vector data member, and we
offer an accessor function that gives clients direct access to it:
```cpp
class Widget {
public:
using DataType = std::vector<double>; // see Item 9 for
… // info on "using"
DataType& data() { return values; }
private:
DataType values;
};
```