0%

C++11 中的 Defaulted 和 Deleted 函数

C++11 标准新特性:Defaulted 和 Deleted 函数

1、Defaulted函数

C++ 的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。这些类的特殊成员函数负责创建、初始化、销毁,或者拷贝类的对象。

如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。 如:

1
2
3
4
5
6
class X{ 
private:
int a;
};

X x;

但是,如果程序员为类 X 显式的自定义了非默认构造函数(带参数),却没有定义默认构造函数的时候,编译器将不再会为它隐式的生成默认构造函数。如果需要用到默认构造函数来创建类的对象时,程序员必须自己显式的定义默认构造函数。 如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class X{ 
public:
X(){}; // 手动定义默认构造函数
X(int i){
a = i;
}
private:
int a;
};

int main(){
X x; // 正确,默认构造函数 X::X() 存在
return 0;
}

上例中的矛盾之处:

  • 原本期望编译器自动生成的默认构造函数需要程序员手动编写,程序员的工作量加大。
  • 手动编写的默认构造函数的代码执行效率比编译器自动生成的默认构造函数低。

为了解决上述矛盾,C++ 11 中提出了 Defaulted 函数。程序员只需在函数声明后加上 “=default;”,就可将该函数声明为 defaulted 函数,编译器将为显式声明的 defaulted 函数自动生成函数体。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class X{ 
public:
X()= default;
X(int i){
a = i;
}
private:
int a;
};

int main(){
X x;
return 0;
}

注意:

  • Defaulted 函数特性仅适用于类的特殊成员函数(四个),且该特殊成员函数没有默认参数。
  • Defaulted 函数既可以在类体里(inline)定义,也可以在类体外(out-of-line)定义。例如:
1
2
3
4
5
6
7
8
9
10
class X{ 
public:
X() = default; //Inline defaulted 默认构造函数
X(const X&);
X& operator = (const X&);
~X() = default; //Inline defaulted 析构函数
};

X::X(const X&) = default; //Out-of-line defaulted 拷贝构造函数
X& X::operator = (const X&) = default; //Out-of-line defaulted 拷贝赋值运算符
  • 参考链接里有关于 delete 指向派生类对象指针时的内存泄漏问题,值得学习。

2、Deleted 函数

为了能够让程序员显式的禁用某个函数,C++11 标准引入了一个新特性:deleted 函数。程序员只需在函数声明后加上“=delete;”,就可将该函数禁用。例如,我们可以将类 X 的拷贝构造函数以及拷贝赋值操作符声明为 deleted 函数,就可以禁止类 X 对象之间的拷贝和赋值。如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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; // 错误,拷贝赋值操作符被禁用
return 0;
}

Deleted 函数特性还可用于禁用类的某些转换构造函数,从而避免不期望的类型转换。 如:

1
2
3
4
5
6
7
8
9
10
11
class X{ 
public:
X(double);
X(int) = delete;
};

int main(){
X x1(1.2);
X x2(2); // 错误,参数为整数 int 类型的转换构造函数被禁用
return 0;
}

Deleted 函数特性还可以用来禁用某些用户自定义的类的 new 操作符,从而避免在自由存储区创建类的对象。如:

1
2
3
4
5
6
7
8
9
10
11
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[] 操作符被禁用
return 0;
}

注意:

  • 必须在函数第一次声明的时候将其声明为 deleted 函数,否则编译器会报错。即对于类的成员函数而言,deleted 函数必须在类体里(inline)定义,而不能在类体外(out-of-line)定义。