Item 12: updating,... i'm surprised with c++11 new feature about reference qualifier

This commit is contained in:
racaljk 2018-06-08 17:35:17 +08:00
parent 0f2a7d19a3
commit 7cbcc69900
3 changed files with 65 additions and 3 deletions

View File

@ -90,7 +90,7 @@ void processPointer<const char>(const char*) = delete;
```
如果你想做得更彻底一些,你还要删除`const volatile void*`和`const volatile char*`重载版本,另外还需要一并删除其他标准字符类型的重载版本:`std::wchar_t`,`std::char16_t`和`std::char32_t`。
有趣的是,如果的类里面有一个函数模板,你可能想用`private`经典的C++98惯例来禁止这些函数模板实例化但是不能这样做因为不能给特化的模板函数指定一个不同的访问级别。如果`processPointer`是类`Widget`里面的模板函数, 你想禁止它接受`void*`参数那么通过下面这样C++98的方法就不能通过编译
有趣的是,如果的类里面有一个函数模板,你可能想用`private`经典的C++98惯例来禁止这些函数模板实例化但是不能这样做因为不能给特化的模板函数指定一个不同(于函数模板)的访问级别。如果`processPointer`是类`Widget`里面的模板函数, 你想禁止它接受`void*`参数那么通过下面这样C++98的方法就不能通过编译
compile:
```cpp
class Widget {
@ -124,4 +124,6 @@ void Widget::processPointer<void>(void*) = delete; // 还是public但是已
+ 比起声明函数为private但不定义使用delete函数更好
+ 任何函数都能`delete`,包括非成员函数和模板实例
_译注本条款`delete``deleted`,`删除`视情况使用,都表示一个意思.`删除函数`和`delete函数`也是如此_
_译注
+本条款`delete``deleted`,`删除`视情况使用,都表示一个意思.`删除函数`和`delete函数`也是如此_
+ 函数模板意指未特化前的源码,模板函数则倾向于模板实例化后的函数

View File

@ -0,0 +1,60 @@
## Item 12:使用override声明重载函数
条款12:使用override声明重载函数
在C++面向对象的世界里,涉及的概念有类,继承,虚函数。这个世界最基本的概念是派生类的虚函数重写基类同名函数。令人遗憾的是虚函数重写可能一不小心就错了。给人感觉语言的这一部分设计观点是墨菲定律不是用来遵守的,只是值得尊敬的。
鉴于"重写"听起来像"重载",尽管两者完全不相关,下面就通过一个派生类和基类来说明什么是虚函数重写:
```cpp
class Base {
public:
virtual void doWork(); // 基类虚函数
};
class Derived: public Base {
public:
virtual void doWork(); // 重写Base::doWork(这里"virtual"是可以省略的)
};
std::unique_ptr<Base> upb = // 创建基类指针
std::make_unique<Derived>(); // 指向派生类对象
// 关于stdmake_unique请
… // 参见Item1
upb->doWork(); // 通过基类指针调用doWork
// 实际上是派生类的doWork
// 函数被调用
```
要想重写一个函数,必须满足下列要求:
+ 基类函数必须是`virtual`
+ 基类和派生类函数名必须完全一样(除非是析构函数
+ 基类和派生类函数参数必须完全一样
+ 基类和派生类函数常量性(constness)必须完全一样
+ 基类和派生类函数的返回值和异常说明(exception specifications)必须兼容
除了这些C++98就存在的约束外C++11又添加了一个
+ 函数的引用限定符reference qualifiers必须完全一样。成员函数的引用限定符是C++11很少抛头露脸的特性所以如果你从没听过它无需惊讶。它可以限定成员函数只能用于左值或者右值。成员函数不需要`virtual`也能使用它们:
```cpp
class Widget {
public:
void doWork() &; //只有*this为左值的时候才能被调用
void doWork() &&; //只有*this为右值的时候才能被调用
};
Widget makeWidget(); // 工厂函数(返回右值)
Widget w; // 普通对象(左值)
w.doWork(); // 调用被左值引用限定修饰的Widget::doWork版本
// (即Widget::doWork &)
makeWidget().doWork(); // 调用被右值引用限定修饰的Widget::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?

View File

@ -22,7 +22,7 @@
3. [Item 9:优先考虑别名声明而非typedefs](https://github.com/racaljk/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item9.md)
4. [Item 10:优先考虑限域枚举而非未限域枚举](https://github.com/racaljk/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item10.md) _revised_
5. [Item 11:优先考虑使用deleted函数而非使用未定义的私有声明](https://github.com/racaljk/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item11.md)
6. Item 12:使用override声明重载函数
6. [Item 12:使用override声明重载函数](https://github.com/racaljk/EffectiveModernCppChinese/blob/master/3.MovingToModernCpp/item12.md)_updating_
7. Item 13:优先考虑const_iterator而非iterator
8. Item 14:如果函数不抛出异常请使用noexcept
9. Item 15:尽可能的使用constexpr