C++开发初级


1084 浏览 5 years, 11 months

3.1.4 默认构造函数

版权声明: 转载请注明出处 http://www.codingsoho.com/
默认构造函数

默认构造函数没有参数,也叫做零参数构造函数。使用默认构造函数可以在客户不指定值的情况下初始化数据成员的值。

什么时候需要默认构造函数

考虑一下对象数组。创建对象数组需要完成两个任务:为所有对象分配连续内存空间,为每个对象调用默认构造函数。C++没有提供任何语法让创建数组的代码直接调用不同构造函数。

例如,如果您没有定义SpreadsheetCell类的默认构造函数,下面的代码将无法编译:

SpreadsheetCell cells[3]; // FAILS compile without default constructor
SpreadsheetCell* myCellp = new SpreadsheetCell[10]; // Also FAILS

对于基于堆栈的数组,可以使用下面的初始化器(initializer)绕过这个限制:

SpreadsheetCell cells[3] = {SpreadsheetCell(0), SpreadsheetCell(23), SpreadsheetCell(41)} ; 

然而,如果想要创建某个类的对象数组,最好还是定义类的默认构造函数。

如果您想要在STL容器(例如std::vector)中存储类,也需要默认构造函数。STL容器将在后面详细讨论。

当在其他类中创建类对象的时候,也可以使用默认构造函数,本节中的“构造函数初始化器”部分将讲述这一内容。

最后,如果某个类是继承层次结构的基类,默认构造函数会带来很多方便。在此情况下,子类可以通过默认构造函数方便地初始化基类,后面详细讲述这一内容。

如何编写默认构造函数

下面是具有默认构造函数的SpreadsheetCell类的部分定义:

class SpreadsheetCell
{
 public:
  SpreadsheetCell();
};

代码取自 SpreadsheetCellDefaultCtor\SpreadsheetCell.h

下面的代码首次实现了默认构造函数:

SpreadsheetCell::SpreadsheetCell()
{
  mValue = 0;
  mString = "";
}

代码取自 SpreadsheetCellDefaultCtor\SpreadsheetCell.cpp

在堆栈上可以这样使用默认构造函数:

  SpreadsheetCell myCell;
  myCell.setValue(6);
  cout << "cell 1: " << myCell.getValue() << endl;

代码取自 SpreadsheetCellDefaultCtor\SpreadsheetCellTest.cpp

前面的代码创建了一个名为myCell的新SpreadsheetCell,设置并输出值。与堆栈中对象的其他构造函数不同,调用默认构造函数不需要使用函数调用的语法。根据其他构造函数的语法,您或许会试着这样调用默认构造函数:

  SpreadsheetCell myCell(); // Wrong, but compile pass
  myCell.setValue(6); // However, this line compile fail
  cout << "cell 1: " << myCell.getValue() << endl;

但是试图调用默认构造函数的行可以编译,但是后面的行无法编译。问题在于编译器实际上将第一行当作函数声明,函数名为myCell,没有参数,返回值为SpreadsheetCell对象。当编译第二行时,编译器认为您将函数名当对象使用!

提示 : 当在堆栈中创建对象时,调用默认构造函数不需要使用圆括号.
然而,堆中的对象使用默认构造函数时,需要使用函数调用语法:

  // Note the function-call syntax
  SpreadsheetCell* myCellp = new SpreadsheetCell();

不要浪费大量时间去考虑C++为什么要对堆中的对象以及堆栈中的对象使用不同的语法调用默认构造函数,这正是C++成为令人激动的语言的原因之一。

编译器生成的默认构造函数

本章的第一个SpreadsheetCell类的定义如下所示:

// SpreadsheetCell.h
class SpreadsheetCell
{
 public:
  void setValue(double inValue);
  double getValue() const;

 protected:
  double mValue;
};

代码取自 SpreadsheetCellNumOnly\SpreadsheetCell.h

这个定义没有声明任何默认构造函数,但以下代码仍然可以正常运行:

  SpreadsheetCell myCellp;
  myCellp.setValue(6);

下面的定义与前面的定义相同,只是添加了一个显式构造函数,用一个double做参数。这个定义仍然没有显式声明默认构造函数:

class SpreadsheetCell
{
 public:
  SpreadsheetCell(double initialValue);
};

代码取自 SpreadsheetCellCtors\SpreadsheetCell.h

使用这个定义,下面的代码将无法编译

  SpreadsheetCell myCellp;
  myCellp.setValue(6);

为什么会这样?原因在于如果您没有指定任何构造函数,编译器将自动生成一个没有参数的构造函数。类所有的对象都可以调用编译器生成的默认构造函数,但不会初始化语言的原始类型,例如int和double。
虽然如此您也可以用它来创建类的对象。然而,如果您声明了一个默认构造函数或者其他构造函数,编译器就不会再自动生成默认构造函数。

默认构造函数与无参构造函数是一回事。术语“默认构造函数”并不是说如果您没有声明任何构造函数就会自动生成一个构造函数,而是指如果没有参数,构造函数采用默认值。

显式的默认构造函数(仅限C++11)

在较早版本的C++中,如果类需要一些接受参数的显式构造函数,同时还需要一个什么都不做的默认构造函数,就必须显式地编写空的默认构造函数,如下所示:

class MyClass
{
  MyClass() {};
  MyClass(int i);
}

然而,接口文件最好只包含公有方法的声明而不包含任何实现。前面的类定义与此冲突。解决方法是按照下面的方式定义类:

class MyClass
{
  MyClass();
  MyClass(int i);
}

在实现文件中,空的默认构造函数的实现如下:

MyClass::MyClass(){}

为了避免手动编写空默认构造函数,C++11引入了显式默认构造函数(explicitly defaulted constructor)的概念。从而让您可以按照如下的方法编写类的定义,而不需要在实现文件中实现:

class MyClass
{
  MyClass() = default;
  MyClass(int i);
}

MyClass定义了接受一个整数的定制构造函数。然而,由于使用了default关键字,编译器仍然会生成一个标准的由编译器生成的默认构造函数。

显式删除构造函数(仅限C++11)

C++11还支持显式删除构造函数(explicitly deleted constructors)的概念。例如,您想要定义一个类,这个类没有任何构造函数,您也不想让编译器生成默认构造函数。在此情况可以显式删除默认构造函数:

class MyClass
{
  MyClass() = delete;
}