C++11标准中的 deleted 函数
Deleted 函数
背景问题
对于 C++ 的类,如果程序员没有为其定义特殊成员函数,那么在需要用到某个特殊成员函数的时候,编译器会隐式的自动生成一个默认的特殊成员函数,比如拷贝构造函数,或者拷贝赋值操作符。例如:
清单 10
class X{
public:
X();
};
int main(){
X x1;
X x2=x1; // 正确,调用编译器隐式生成的默认拷贝构造函数
X x3;
x3=x1; // 正确,调用编译器隐式生成的默认拷贝赋值操作符
}
在清单 10 中,程序员不需要自己手动编写拷贝构造函数以及拷贝赋值操作符,依靠编译器自动生成的默认拷贝构造函数以及拷贝赋值操作符就可以实现类对象的拷贝和赋值。这在某些情况下是非常方便省事的,但是在某些情况下,假设我们不允许发生类对象之间的拷贝和赋值,可是又无法阻止编译器隐式自动生成默认的拷贝构造函数以及拷贝赋值操作符,那这就成为一个问题了。
Deleted 函数的提出
为了能够让程序员显式的禁用某个函数,C++11 标准引入了一个新特性:deleted 函数。程序员只需在函数声明后加上“=delete;”,就可将该函数禁用。例如,我们可以将类 X 的拷贝构造函数以及拷贝赋值操作符声明为 deleted 函数,就可以禁止类 X 对象之间的拷贝和赋值。
清单 11
class X{
public:
X();
X(const X&) = delete; // 声明拷贝构造函数为 deleted 函数
X& operator = (const X &) = delete; // 声明拷贝赋值操作符为 deleted 函数
};
int main(){
X x1;
X x2=x1; // 错误,拷贝构造函数被禁用
X x3;
x3=x1; // 错误,拷贝赋值操作符被禁用
}
在清单 11 中,虽然只显式的禁用了一个拷贝构造函数和一个拷贝赋值操作符,但是由于编译器检测到类 X 存在用户自定义的拷贝构造函数和拷贝赋值操作符的声明,所以不会再隐式的生成其它参数类型的拷贝构造函数或拷贝赋值操作符,也就相当于类 X 没有任何拷贝构造函数和拷贝赋值操作符,所以对象间的拷贝和赋值被完全禁止了。
Deleted 函数定义语法
Deleted 函数是 C++11 标准引入的函数定义新语法,deleted 函数定义的语法如图 2 所示:
图 2. Deleted 函数定义语法图
Deleted 函数的用法及示例
Deleted 函数特性还可用于禁用类的某些转换构造函数,从而避免不期望的类型转换。在清单 12 中,假设类 X 只支持参数为双精度浮点数 double 类型的转换构造函数,而不支持参数为整数 int 类型的转换构造函数,则可以将参数为 int 类型的转换构造函数声明为 deleted 函数。
清单 12
class X{
public:
X(double);
X(int) = delete;
};
int main(){
X x1(1.2);
X x2(2); // 错误,参数为整数 int 类型的转换构造函数被禁用
}
Deleted 函数特性还可以用来禁用某些用户自定义的类的 new 操作符,从而避免在自由存储区创建类的对象。例如:
清单 13
#include
using namespace std;
class X{
public:
void *operator new(size_t) = delete;
void *operator new[](size_t) = delete;
};
int main(){
X *pa = new X; // 错误,new 操作符被禁用
X *pb = new X[10]; // 错误,new[] 操作符被禁用
}
必须在函数第一次声明的时候将其声明为 deleted 函数,否则编译器会报错。即对于类的成员函数而言,deleted 函数必须在类体里(inline)定义,而不能在类体外(out-of-line)定义。例如:
清单 14
class X {
public:
X(const X&);
};
X::X(const X&) = delete; // 错误,deleted 函数必须在函数第一次声明处声明
虽然 defaulted 函数特性规定了只有类的特殊成员函数才能被声明为 defaulted 函数,但是 deleted 函数特性并没有此限制。非类的成员函数,即普通函数也可以被声明为 deleted 函数。例如:
清单 15
int add (int,int)=delete;
int main(){
int a, b;
add(a,b); // 错误,函数 add(int, int) 被禁用
}
值得一提的是,在清单 15 中,虽然 add(int, int)函数被禁用了,但是禁用的仅是函数的定义,即该函数不能被调用。但是函数标示符 add 仍是有效的,在名字查找和函数重载解析时仍会查找到该函数标示符。如果编译器在解析重载函数时,解析结果为 deleted 函数,则会出现编译错误。例如:
清单 16
#include <iostream>
using namespace std;
int add(int,int) = delete;
double add(double a,double b){
return a+b;
}
int main(){
cout << add(1,3) << endl; // 错误,调用了 deleted 函数 add(int, int)
cout << add(1.2,1.3) << endl;
return 0;
}
结束语
本文详细介绍了 C++11 特性 deleted 函数。结合小编前一篇文章 defaulted ,巧妙地对 C++ 已有的关键字 default 和 delete 的语法进行了扩充,引入了两种新的函数定义方式:在函数声明后加 =default 和 =delete。通过将类的特殊成员函数声明为 defaulted 函数,可以显式指定编译器为该函数自动生成默认函数体。通过将函数声明为 deleted 函数,可以禁用某些不期望的转换或者操作符。Defaulted 和 deleted 函数特性语法简单,功能实用,是对 C++ 标准的一个非常有价值的扩充。
另外本人从事在线教育多年,将自己的资料整合建了一个QQ群,对于有兴趣一起交流学习c/c++的初学者可以加群:941636044,里面有大神会给予解答,也会有许多的资源可以供大家学习分享,欢迎大家前来一起学习进步!