缺省构造函数

缺省构造函数C++及其他一些面向对象程序设计语言中,对象的不需要参数即可调用的构造函数

概述

对象生成时如果没有显式地调用构造函数,则缺省构造函数会被自动调用。C++标准规定,如果构造函数没有参数(nullary),或者构造函数的所有参数都有缺省值(default value),都算作缺省构造函数。[1]一个只能有一个缺省构造函数。

例如,显式定义、使用“缺省构造函数”:

class MyClass
{
  public:
    MyClass();             // constructor declared

  private:
    int x;
};

MyClass :: MyClass()       // constructor defined   
{
    x = 100;
}

int main()
{
    MyClass object;        // object created 
}                          // => default constructor called automatically

下述例子是动态生成对象时,显示调用了缺省构造函数:

int main()
{
    MyClass * pointer = new MyClass();  // object created 
}                                       // => default constructor called automatically
    MyClass (int i = 0) {}

C++中,缺省构造函数会在下列情形被自动调用:

  • 对象被定义时无参数,例如:MyClass x;; 或动态分配对象时无参数列表,例如:new MyClassnew MyClass(); 缺省构造函数用于初始化对象。
  • 对象数组被定义时,例如:MyClass x[10];; 或被动态分配时,例如:new MyClass [10];缺省构造函数初始化数组的每个对象。
  • 当派生类在其初始化列表(initializer list)中没有显式调用其基类对象的构造函数时,基类的缺省构造函数被自动调用。
  • 当一个类的构造函数的初始化列表中没有显式调用其对象成员(object component或object-valued field)的构造函数时,对象成员的缺省构造函数被自动调用。
  • C++标准程序库中,一些容器的填充值使用了值对象的缺省构造函数,如果这些值没有显式地给出。例如:vector<MyClass>(10);用10个元素初始化vector,这些元素用其缺省构造函数来初始化。

在上述这些情形中,如果被初始化地对象没有缺省构造函数,则编译时报错。

如果一个类没有显式定义构造函数,编译器将为其隐式地定义缺省构造函数(下文给出了例外情形)。隐式定义的缺省构造函数的函数体为空。例如:

class MyClass
{
    int x;                 // no constructor          
};                         // => the compiler produces an (implicit) default constructor
int main()
{
    MyClass object;        // no error: the (implicit) default constructor is called
}

如果类显式地定义了一些构造函数,但不是缺省构造函数,编译器则不会为该类隐式定义缺省构造函数。该类就没有缺省构造函数了。也就是说,并不是所有类都有缺省构造函数。这是很多常见错误的原因。例如:

class MyClass 
{
  public:
    MyClass (int y);         // a constructor     

  private:
    int x;
};
MyClass :: MyClass (int y)
{
    x = y;
}
int main()
{
    MyClass object(100);     // constructor called
    MyClass *pointer;        // for declaration do not need to know about existing constructors
    pointer = new MyClass(); // error: no default constructor    
    return 0;
}

如果类的基类没有缺省构造函数,那么编译器也不会为该类隐式地定义缺省构造函数。因为该类即使隐式地定义缺省构造函数,也无法初始化其基类。例如:

class A
{
public:
	explicit A(int){}
};

class B: public A
{
};

int main()
{
	B b1;         //编译报错
}

如果类的基类的缺省构造函数为私有,那么编译器会为该类隐式地定义缺省构造函数,但编译报错“cannot access private member declared in class 基类名字”。例如:

class A
{
   A(){};
public:
	
};

class B: public A
{
};

int main()
{
	B b1;         //编译报错
}

对于union数据类型,如果它包含了一个成员有非缺省的构造函数,那么这个union类型的构造函数必须显式调用这个成员类型的构造函数。例如:

struct bar {
	bar() {};
	bar(int) {};
};
union foo { 
	int i;
	float j;
	bar b;
        // foo(): b{ } { };    //只有增加这行,才能成功地构造一个foo类型的对象
};
foo vvv;         // syntax error: attempting to reference a deleted function

进阶概念

class-name () = default;称作“显式地被缺省”(explicitly-defaulted)。

class-name () = delete;称作“缺省构造函数被删除”(The default constructor is deleted.)。

平凡的默认构造函数(Trivial default constructor)是不执行任何操作的构造函数。 所有与C语言兼容的数据类型(POD 类型)都是平凡的默认可构造。如果满足以下所有条件,则类T的默认构造函数是平凡的(即不执行任何操作):

  • 构造函数不是用户提供的(即,在其第一个声明中隐式定义或默认)。
  • T 没有虚成员函数。
  • T 没有虚基类。
  • T 没有带有缺省的初始值设定项的非静态成员(自 C++11 起)
  • T 的每个直接基类都有一个平凡的默认构造函数。
  • 类类型(或其数组)的每个非静态成员都有一个平凡的默认构造函数。

隐式声明的默认构造函数:如果没有为类类型提供任何类型的用户声明的构造函数,则编译器总是将默认构造函数声明为其类的内联公共成员。如果存在一些用户声明的构造函数,用户仍然可以强制编译器使用关键字 default自动生成默认构造函数。(自 C++11 起)

隐式定义的默认构造函数:如果隐式声明或“显式地被缺省”的缺省构造函数未被删除,则由编译器定义(即生成函数体并编译),与具有空主体和空初始值列表的用户定义构造函数具有相同的效果。也就是说,它调用该类的基类和非静态成员的默认构造函数。 如果存在一些用户定义的构造函数,用户仍然可以强制编译器自动生成默认构造函数,否则将使用关键字 default 隐式声明该默认构造函数。

被删除的缺省构造函数(Deleted default constructor),如果满足以下任一条件,则类T的隐式声明或显式缺省的默认构造函数为已删除(C++11起):

  • T 是一个union,其所有变体成员都是const限定类型(或是其多维数组)。
  • T 是一个非union的类,且所有匿名联合成员都是const限定类型(或其多维数组)。
  • T 有一个引用类型的非静态数据成员,没有缺省初始化值(C++11 起)。
  • T 具有const限定类型的非变体、非静态、非const缺省可构造的数据成员(或其多维数组),没有缺省初始化值 (C++11 起)。
  • T 具有类类型为M(或其多维数组)的潜在可构造子对象,使得M有一个析构函数,该析构函数已被删除或无法访问缺省构造函数

如果不存在用户定义的构造函数并且隐式声明的缺省构造函数并不平凡,则用户仍然可以使用关键字delete禁止编译器自动生成隐式定义的缺省构造函数。

 
struct F
{
    int& ref; // reference member
    const int c; // const member
    // F::F() is implicitly defined as deleted
};

参考文献

  1. ^ C++ standard, ISO/IEC 14882:1998, 12.1.5