类的构造函数
每个类都分别定义了它的对象被初始化的方式, 类通过一个或几个特殊的成员函数来控制器对象的初始化过程, 这些函数叫做构造函数。
构造函数的任务是初始化对象的数据成员, 无论何时只要类的对象被创建, 就会执行构造函数。
默认构造函数
默认构造函数是在未提供显式初始值时, 用来创建对象的构造函数。
注意: 当且仅当没有定义任何构造函数时, 编译器才会提供默认构造函数。 为类定义了构造函数后, 程序员就必须为它提供默认构造函数, 否则上面的声明将出错。
析构函数
用构造函数创建对象后, 程序负责跟踪该对象, 直到其过期为止。 对象过期时, 程序将自动调用一个特殊的成员函数, 析构函数, 来完成清理工作。
派生类的构造函数
在定义派生类时, 派生类并没有把基类的构造函数继承下来。
因此, 派生类的构造函数同时需要对继承的基类成员进行初始化。
派生类构造函数的定义:
派生类名(形式参数列表):基类名(基类构造函数实参列表)
{
派生类初始化函数体
}
- 形式参数列表中应包含基类成员和派生类成员。
- 基类名(基类构造函数实参列表), 即调用基类的构造函数。
派生类构造函数的调用顺序:
- 调用基类的构造函数。
- 执行派生类的构造函数。
派生类的析构函数
派生类并没有继承基类的析构函数, 所以也需要通过派生类的析构函数去调用基类的析构函数。
派生类中的析构函数, 用来清理派生类中增加的成员。 基类成员依然由基类的析构函数清理。
在执行派生类的析构函数时, 编译器会自动调用基类的析构函数。
派生类析构函数的调用顺序:
- 执行派生类的析构函数
- 调用基类的析构函数
组合关系的派生类
派生类A和类B是组合关系, 即类A中有类B的子对象。
class A
{
B b1_;
}
这种情况下, 可以在类A的构造函数中显式地初始化类B的子对象。
类名(形式参数列表):子对象名(子对象构造函数实参列表)
{
类初始化函数体
}
注意: 如果类B有默认构造函数, 或参数全是默认参数的构造函数, 或有无参数的构造函数, 那么类A的构造函数中可以不用显式初始化子对象。 编译器会自动调用B的构造函数进行初始化。
构造函数和析构函数的调用顺序:
- 调用基类构造函数
- 调用子对象构造函数
- 执行派生类的构造函数
- 执行派生类的析构函数
- 调用子对象的析构函数
- 调用基类的析构函数
#include<iostream>
using namespace std;
class Base
{
protected:
int bnum1_;
public:
//基类的构造函数和析构函数
Base()
{
bnum1_ = 0;
cout << "调用基类的默认构造函数" << endl;
}
Base(int bnum1)
{
bnum1_ = bnum1;
cout << "调用基类的构造函数" << endl;
}
~Base()
{
cout << "调用基类的析构函数" << endl;
}
void print()
{
cout << "bnum1_: " << bnum1_ << endl;
}
};
class Derived : public Base
{
private:
int dnum1_;
public:
//派生类的构造函数,需要使用:Base()调用基类的构造函数,不写的话默认调用基类的默认构造函数
//先基类,再派生类
Derived() :Base()
{
dnum1_ = 0;
cout << "调用派生类的默认构造函数" << endl;
}
Derived(int bnum1, int dnum1) : Base(bnum1)
{
dnum1_ = dnum1;
cout << "调用派生类的构造函数" << endl;
}
//派生类的析构函数,编译器会自动地调用基类的析构函数
//先派生类,再基类
~Derived()
{
cout << "调用派生类的析构函数" << endl;
}
void print()
{
cout << "bnum1_: " << bnum1_ << " dnum1_: " << dnum1_ << endl;
}
};
class B
{
public:
int b_;
B()
{
b_ = 0;
cout << "调用B的默认构造函数" << endl;
}
B(int b)
{
b_ = b;
cout << "调用B的构造函数" << endl;
}
~B()
{
cout << "调用B的析构函数" << endl;
}
void print()
{
cout << "b_: " << b_ << endl;
}
};
class A : public Base
{
private:
int a_;
//子对象
B b1;
public:
//构造函数调用顺序: 先基类,再是子对象,再是派生类
A(): b1(), Base()
{
a_ = 0;
cout << "调用A的默认构造函数" << endl;
}
//使用的是子对象的名称,而不是子对象的类名
A(int b, int bnum1, int a) : b1(b), Base(bnum1)
{
a_ = a;
cout << "调用A的构造函数" << endl;
}
//析构函数调用顺序: 先派生类,再是子对象,再是基类
~A()
{
cout << "调用A的析构函数" << endl;
}
void print()
{
cout << "b_: " << b1.b_ << " bnum1_: "<< bnum1_ << " a_: " << a_ << endl;
}
};
int main()
{
Base base1;
Base base2(2);
base1.print();
base2.print();
Derived d1;
Derived d2(3, 8);
d1.print();
d2.print();
B b1;
A a1;
b1.print();
a1.print();
B b2(3);
A a2(6, 8, 10);
b2.print();
a2.print();
return 0;
}