Effective Modern C++ Item 11 的学习和解读。
如果你的代码里有一些特殊的函数不想被别人调用,一般来说你不申明它即可。但是,有些特殊的成员函数,C++ 会自动申明,比如拷贝构造函数、拷贝赋值 *** 作。
C++98 的做法C++98 的做法是将它们申明为私有并且不定义它们。以 IO 流为例,IO 流的基础类是 basic_ios,输入输出流都是继承与它。IO 流的拷贝是被阻止的(例如输入流 istream对象,表示输入数值,一些可能已经读入到内存中,一些可能还未读入,如果要复制一个输入流,是复制哪一部分?为了避免这样的问题,直接阻止 IO 流的拷贝)。
为了阻止 IO 流的复制,C++98 处理如下:
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
...
private:
basic_ios(const basic_ios& ); // not defined
basic_ios& operator=(const basic_ios&); // not defined
};
将这些函数申明为私有并不定义,若这些函数被调用,在链接阶段将会因为没有定义而失败。
C++11 的做法C++11 中有更好的处理方法,给这些函数标记为 “= delete” ,表明它们是被删除的函数:
template <class charT, class traits = char_traits<charT> >
class basic_ios : public ios_base {
public:
...
basic_ios(const basic_ios& ) = delete;
basic_ios& operator=(const basic_ios&) = delete;
...
};
删除函数不可以任何方式使用,即使是成员函数和友元函数。使用这些函数将导致编译报错。这比 C++98 中的方法诊断出错误的时间提前到编译阶段。
将 delete 函数申明为 public 的原因是:C++ 先检查访问权限,再检查 delete 状态。如果申明为 private,当使用删除的私有函数时,有些编译器只会报出这些函数是私有的,但其实更明确的含义是这些是删除函数,不期望被使用。
delete 函数优势delete 函数的优势是它可以应用于任何函数,而不仅仅是成员函数。例如你有一个函数,只接受输入为 int 类型:
bool isLucky(int number);
但是,C++ 中很多类型都可以隐式转换为 int 类型。如下调用都可以通过:
isLucky('a');
isLucky(true);
isLucky(3.5);
C++11 的处理方法是将他们标记成 delete:
bool isLucky(int number); // original function
bool isLucky(char) = delete; // reject chars
bool isLucky(bool) = delete; // reject bools
bool isLucky(double) = delete; // reject doubles and floats
上面将参数为 double 的函数标记成 delete,可以阻止 float 和 double 两种参数的调用:C++总是倾向于将 float 转换为 double。
delete 函数的另一个优势体现在使用模板时候。如下面例子:
template<typename T>
void processPointer(T* ptr);
假设你想阻止 void* 和 char* 类型的特例,将这些函数的实例化标记成 delete 即可:
template<>
void processPointer<void>(void*) = delete;
template<>
void processPointer<const void>(const void*) = delete;
template<>
void processPointer<char>(char*) = delete;
template<>
void processPointer<const char>(const char*) = delete;
如果你在一个类内部有一个函数模板,你想通过声明它们为私有来禁止某些实现,是行不通的:因为模板的特例化无法在类的作用域内定义:
#include
class A {
public:
A();
template<typename T>
void processPointer(T* ptr) {
std::cout << ptr << std::endl;
}
private:
template<>
void processPointer<void>(void*);
};
int main()
{
return 0;
}
// 报错信息:
main.cpp:12:12: error: explicit specialization in non-namespace scope 'class A'
12 | template<>
| ^
main.cpp:13:8: error: template-id 'processPointer' in declaration of primary template
13 | void processPointer<void>(void*);
|
使用 public 和 delete 方式则没有问题:
class A {
public:
A();
template<typename T>
void processPointer(T* ptr) {
std::cout << ptr << std::endl;
}
};
template<>
void A::processPointer<void>(void*) = delete;
因此,选择使用 delete 函数吧。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)