再探默认构造函数

今天使用派生类时又发现了一个问题,基类和派生类的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Base
{
public:
Base(int a)
{ }
};

class Derived : public Base
{
public:
Derived()
{ }
};

还没编译,Creator就已经报错了
2
编译时Derived类会报错: error: no matching function for call to ‘Base::Base()’

这个问题的实质还是默认构造函数。我们都知道,如果一个类没有构造函数,编译器会为我们自动创建一个默认构造函数,这个函数没有参数,什么都没做。但是,当我们实现一个构造函数之后,编译器就不会创建,因此Base没有默认构造函数。

默认构造函数就是在调用时不需要explicitly传入实参的构造函数

上面我定义的Base(int a)是构造函数,而不是默认构造函数。 C++中,最多有一个默认构造函数,刻意是编译器生成,也可以是我们自己定义的。我们自己定义的默认构造函数只能是两种:

  1. 无参 2. 有参数,但参数都有默认值

比如下面两种都是默认构造函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Base
{
public:
Base()
{ }
};
// 或者有默认值的有参构造函数
class Base
{
public:
Base(int a=0)
{ }
};

但是不要把这两种构造函数都放入类里,因为最多只能有一个默认构造函数。不实例化不报错,一旦实例化就会报错: 指定了多个默认构造函数,对重载函数调用不明确

派生类构造函数对基类初始化

再回到上面的问题

1
2
3
4
5
6
7
8
9
10
11
12
13
class Base{
public:
Base(int value){}
};

class Derived : public Base
{
public:
Derived()
{ }
private:
int m_value;
};

现在加入派生类,以上的代码会报错: error: constructor for ‘Derived’ must explicitly initialize the base class ‘Base’ which does not have a default constructor

如果基类没有默认构造函数,那么编译器也不会为派生类隐式地定义默认构造函数, 就会出现上面的错误。对这种错误,有两种修改方法:

  1. 对于有参的构造函数,参数赋默认值。也就是改成默认构造函数:把Base(int a)改为Base(int a=0)

  2. 最常用,也是Qt中所用的:在派生类的构造函数后,用列表初始化的方式调用基类构造函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Derived : public Base
    {
    public:
    Derived():
    Base(m_value)
    { }
    private:
    int m_value;
    };

上面的问题,可以总结如下:

  • 基类没有显式声明构造函数或者有一个无参数的构造函数,派生类构造函数可以不用对基类初始化,即忽略基类的构造函数
  • 基类的构造函数全是有参数的,派生类必须至少实现一个基类的构造函数,例如Qt中常见的:
1
2
3
4
5
explicit MainWindow(QWidget* parent=0);
// QMainWindow构造函数都有参数
MainWindow::Mainwindow(QWidget* parent):
QMainWindow(parent),
ui(new Ui::MainWindow)

这种方式解决的问题是:使用派生类创建一个对象后,怎样初始化从基类中继承过来的数据成员?(基类的构造函数是不能被继承的)

这种代码的具体格式:

1
2
3
4
派生类::派生类构造函数(总参数列表):基类构造函数(参数列表)
{
派生类中的数据成员初始化;
}

注意:如果没有基类和派生的关系,就不能用这种初始化格式,否则报错。

C++11中的default关键字

之前说了,如果我们显式声明构造函数,编译器就不会生成默认构造函数。 但是有时候,我们反而需要一个默认构造函数,比如下面的情况:

1
2
3
4
5
6
7
8
9
10
11
12
class C
{
public:
C(int f)
{
qDebug()<<"construct" ;
}
int a;
long b;
};

C obj;

很显然会报错,因为不存在默认构造函数了。我们可以加一个构造函数:C()=default;,这样编译就通过了

但是以下用法都会报错:

1
2
Base(double value=0)=default;
Base(float value)=default;

這是因为= default只能被加在 沒有默认参数 的special member function后面,Special member function包含: Default constructor, Destructor, Copy constructor, Copy assignment, Move constructor, Move assignment