本条款的原英文标题是 “Prefer deleted functions to private undefined ones”,不太好理解,笔者根据内容做了个转述。想表达的是:当你需要屏蔽掉某个特定的函数时,使用C++11的 delete
特性而不是像C++98中将函数声明为 private
且不给定义 。 如果你在给其他开发者提供函数或库,有时你会希望阻止他们调用某些函数,但这些函数又被C++自动生成了。该问题主要集中于复制构造函数和赋值运算符 上(先不考虑C++11新引入的移动构造函数)。例如,iostream
中的输入和输出流都继承于 basic_ios
类。我们不希望这些流被复制,所以要禁用掉它的复制构造函数和赋值运算符。C++98中声明如下:
template < class charT , class traits = char_traits< charT> >
class basic_ios : public ios_base {
public :
. . .
private :
basic_ios ( const basic_ios& ) ;
basic_ios& operator = ( const basic_ios& ) ;
} ;
C++98时代,做到该效果的方法正如以上所示:声明它们,以覆盖掉编译器自动生成的版本;访问权限设为 private
,防止无关类调用它们;只声明不定义,防止 friend
类或自己内部调用它们 。结果来说,当无关类试图调用时,会报编译错误 ;友元类或自身内部试图调用时,会报链接错误 。 C++11中要实现该效果可以用一个更好的方法:delete
关键字 。例如当前MSVC的 basic_ios
是这样实现的: 此时不需再用 private
限制访问权限的方式禁止外部调用。相反地,它们一般应该被定义为 public
。因为函数的访问权限会在是否被 delete
状态之前检查,如果声明为 private
,外部调用会报访问权限的错误,而友元类调用会报 delete
的错误(类似C++98的情况)。而我们希望的是所有情况都统一报 delete
错误。 delete
的另一个重要优势在于:任何函数都可以被声明为 delete
,而 private
只能作用于类的成员函数上 。例如有一个非成员函数 bool isLucky(int number)
判断输入整数是否是“幸运数”。可惜由于隐式转换的存在,传入一个 a
,或 true
,或 3.5
的调用都是合法的。在C++11中,我们可以用 delete
重载函数的方式明确拒绝这些参数:
bool isLucky ( int number) { . . . }
if ( isLucky ( 1 ) ) . . .
if ( isLucky ( 'a' ) ) . . .
if ( isLucky ( true ) ) . . .
if ( isLucky ( 3.5 ) ) . . .
bool isLucky ( char ) = delete ;
bool isLucky ( bool ) = delete ;
bool isLucky ( double ) = delete ;
也许你已经联想到了接下来的发展:delete
还能用于阻止模板对于某种特定参数类型的实例化 !例如你要写一个处理裸指针的模板函数,我们知道 void*
和 char*
类型的指针比较特殊(前者不能解引用,后者一般指字符串而非单字符),一般需要单独处理。假设这里就是要拒绝这两种参数,你可以通过写出模板的特化形式,并将它们 delete
掉来实现:
template < typename T >
void processPointer ( T* ptr) { . . . }
template < >
void processPointer< void > ( void * ) = delete ;
template < >
void processPointer< char > ( char * ) = delete ;
int * pi, char * pc, void * pv;
processPointer ( pi) ;
processPointer ( pc) ;
processPointer ( pv) ;
C++98无法处理非成员函数。然而有趣的是就算是类内的成员模板函数,它也无法处理,因为无法将模板的访问权限为 public
而将其特化版本设为 private
。delete
方法不会有这个问题,因为二者都是被声明为 public
的。
总结
用 delete
,不要用 private undefined。 包括非成员函数和模板实例化函数都能被 delete
掉。