因此,一般情况下我们需要将基类的析构函数定义为虚函数,当利用 delete 语句删除指向派生类对象的基类指针时,系统会调用相应的派生类的析构函数(实现多态性),从而避免内存泄露。但是编译器隐式自动生成的析构函数都是非虚函数,这需要由程序员手动的为基类 X 定义虚析构函数,例如:
  清单 8
  class X {
  public:
  virtual ~X(){};     // 手动定义虚析构函数
  private:
  int x;
  };
  class Y: public X {
  private:
  int y;
  };
  int main(){
  X* x = new Y;
  delete x;
  }
  在清单 8 中,由于程序员手动为基类 X 定义了虚析构函数,当利用 delete 语句删除指向派生类对象的基类指针 x 时,系统会调用相应的派生类 Y 的析构函数(由编译器隐式自动生成)以及基类 X 的析构函数,从而将派生类对象完整的销毁,可以避免内存泄露。
  但是,在清单 8 中,程序员需要手动的编写基类的虚构函数的定义(哪怕函数体是空的),增加了程序员的编程工作量。更值得一提的是,手动定义的析构函数的代码执行效率要低于编译器自动生成的析构函数。
  为了解决上述问题,我们可以将基类的虚析构函数声明为 defaulted 函数,这样可以显式的指定编译器为该函数自动生成函数体。例如:
  清单 9
  class X {
  public:
  virtual ~X()= defaulted; // 编译器自动生成 defaulted 函数定义体
  private:
  int x;
  };
  class Y: public X {
  private:
  int y;
  };
  int main(){
  X* x = new Y;
  delete x;
  }
  在清单 9 中,编译器会自动生成虚析构函数 virtual X::X(){},该函数比用户自己定义的虚析构函数具有更高的代码执行效率。
  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 所示:

  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 新特性 defaulted 和 deleted 函数。该特性巧妙地对 C++ 已有的关键字 default 和 delete 的语法进行了扩充,引入了两种新的函数定义方式:在函数声明后加 =default 和 =delete。通过将类的特殊成员函数声明为 defaulted 函数,可以显式指定编译器为该函数自动生成默认函数体。通过将函数声明为 deleted 函数,可以禁用某些不期望的转换或者操作符。Defaulted 和 deleted 函数特性语法简单,功能实用,是对 C++ 标准的一个非常有价值的扩充。