本文将介绍 C++11 标准的两个新特性:defaulted 和 deleted 函数。对于 defaulted 函数,编译器会为其自动生成默认的函数定义体,从而获得更高的代码执行效率,也可免除程序员手动定义该函数的工作量。对于 deleted 函数, 编译器会对其禁用,从而避免某些非法的函数调用或者类型转换,从而提高代码的安全性。本文将通过代码示例详细阐述 defaulted 和 deleted 函数的用法及益处。
  Defaulted 函数
  背景问题

  C++ 的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数以及拷贝赋值运算符。这些类的特殊成员函数负责创建、初始化、销毁,或者拷贝类的对象。如果程序员没有显式地为一个类定义某个特殊成员函数,而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。例如:
  清单 1
  class X{
  private:
  int a;
  };
  X x;
  在清单 1 中,程序员并没有定义类 X 的默认构造函数,但是在创建类 X 的对象 x 的时候,又需要用到类 X 的默认构造函数,此时,编译器会隐式的为类 X 生成一个默认构造函数。该自动生成的默认构造函数没有参数,包含一个空的函数体,即 X::X(){ }。虽然自动生成的默认构造函数仅有一个空函数体,但是它仍可用来成功创建类 X 的对象 x,清单 1 也可以编译通过。
  但是,如果程序员为类 X 显式的自定义了非默认构造函数,却没有定义默认构造函数的时候,清单 2 将会出现编译错误:
  清单 2
  class X{
  public:
  X(int i){
  a = i;
  }
  private:
  int a;
  };
  X x;  // 错误 , 默认构造函数 X::X() 不存在
  清单 2 编译出错的原因在于类 X 已经有了用户自定义的构造函数,所以编译器将不再会为它隐式的生成默认构造函数。如果需要用到默认构造函数来创建类的对象时,程序员必须自己显式的定义默认构造函数。例如:
  清单 3
  class X{
  public:
  X(){};  // 手动定义默认构造函数
  X(int i){
  a = i;
  }
  private:
  int a;
  };
  X x;   // 正确,默认构造函数 X::X() 存在
  从清单 3 可以看出,原本期望编译器自动生成的默认构造函数需要程序员手动编写了,即程序员的工作量加大了。此外,手动编写的默认构造函数的代码执行效率比编译器自动生成的默认构造函数低。类的其它几类特殊成员函数也和默认构造函数一样,当存在用户自定义的特殊成员函数时,编译器将不会隐式的自动生成默认特殊成员函数,而需要程序员手动编写,加大了程序员的工作量。类似的,手动编写的特殊成员函数的代码执行效率比编译器自动生成的特殊成员函数低。
  Defaulted 函数的提出
  为了解决如清单 3 所示的两个问题:1. 减轻程序员的编程工作量;2. 获得编译器自动生成的默认特殊成员函数的高的代码执行效率,C++11 标准引入了一个新特性:defaulted 函数。程序员只需在函数声明后加上“=default;”,可将该函数声明为 defaulted 函数,编译器将为显式声明的 defaulted 函数自动生成函数体。例如:
  清单 4
  class X{
  public:
  X()= default;
  X(int i){
  a = i;
  }
  private:
  int a;
  };
  X x;
  在清单 4 中,编译器会自动生成默认构造函数 X::X(){},该函数可以比用户自己定义的默认构造函数获得更高的代码效率。
  Defaulted 函数定义语法
  Defaulted 函数是 C++11 标准引入的函数定义新语法,defaulted 函数定义的语法如图 1 所示:

  Defaulted 函数的用法及示例
  Defaulted 函数特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数。例如:
  清单 5
  class X {
  public:
  int f() = default;      // 错误 , 函数 f() 非类 X 的特殊成员函数
  X(int) = default;       // 错误 , 构造函数 X(int, int) 非 X 的特殊成员函数
  X(int = 1) = default;   // 错误 , 默认构造函数 X(int=1) 含有默认参数
  };
  Defaulted 函数既可以在类体里(inline)定义,也可以在类体外(out-of-line)定义。例如:
  清单 6
  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
  // 拷贝赋值操作符
  在 C++ 代码编译过程中,如果程序员没有为类 X 定义析构函数,但是在销毁类 X 对象的时候又需要调用类 X 的析构函数时,编译器会自动隐式的为该类生成一个析构函数。该自动生成的析构函数没有参数,包含一个空的函数体,即 X::~X(){ }。例如:
  清单 7
  class X {
  private:
  int x;
  };
  class Y: public X {
  private:
  int y;
  };
  int main(){
  X* x = new Y;
  delete x;
  }
  在清单 7 中,程序员没有为基类 X 和派生类 Y 定义析构函数,当在主函数内 delete 基类指针 x 的时候,需要调用基类的析构函数。于是,编译器会隐式自动的为类 X 生成一个析构函数,从而可以成功的销毁 x 指向的派生类对象中的基类子对象(即 int 型成员变量 x)。
  但是,这段代码存在内存泄露的问题,当利用 delete 语句删除指向派生类对象的指针 x 时,系统调用的是基类的析构函数,而非派生类 Y 类的析构函数,因此,编译器无法析构派生类的 int 型成员变量 y。