类内有两种特殊的函数(当然也要6种的说法,其中5种是构造函数),称为构造函数和析构函数,下面写了两个类来了解一下普通构造函数与析构函数。
代码地址:http://cpp.sh/27lkt
#include <iostream>
using namespace std;
class A {
public:
A(int _m) : m(_m) { cout << "general construct A" << endl; }
~A() { cout << "desctruct A: " << this << endl; }
private:
int m;
};
class B {
public:
B() { cout << "general construct B" << endl; }
B(double b) { cout << "general construct B double" << endl; }
~B() { cout << "destruct B: " << this << endl; }
};
A g_a(10);
int main()
{
cout << "-------------------" << endl;
//A a1; // 匹配A::A()
cout << "---------A a2(2)---------" << endl;
A a2(2);
cout << "---------A a3 = A(3)---------" << endl;
A a3 = A(3);
cout << "---------A* pa4 = new A(4)---------" << endl;
A* pa4 = new A(4);
// A* pa5 = new A[3]; // 匹配A::A()
// A a6[3]; // 匹配A::A()
cout << "---------A* pa7 = (A*)malloc(2)---------" << endl;
A* pa7 = (A*)malloc(2);
cout << "---------B b1---------" << endl;
B b1;
cout << "---------B b2()---------" << endl;
B b2();
cout << "-------- b1 = B()----------" << endl;
b1 = B();
cout << "---------B b3[2]---------" << endl;
B b3[2];
cout << "---------B *pb4 = new B---------" << endl;
B *pb4 = new B;
cout << "---------B *pb5 = new B()---------" << endl;
B *pb5 = new B();
cout << "---------B *pb6 = new B[3]---------" << endl;
B *pb6 = new B[3];
cout << "---------B *pb7 = (B*)malloc(10)--------" << endl;
B *pb7 = (B*)malloc(10);
cout << "---------B b8(5.0)------------------" << endl;
B b8(5.0);
cout << "--------------end------------------" << endl;
return 0;
}
结果输出
general construct A
-------------------
---------A a2(2)---------
general construct A
---------A a3 = A(3)---------
general construct A
---------A* pa4 = new A(4)---------
general construct A
---------A* pa7 = (A*)malloc(2)---------
---------B b1---------
general construct B
---------B b2()---------
-------- b1 = B()----------
general construct B
destruct B: 0x75ecf0afd6af
---------B b3[2]---------
general construct B
general construct B
---------B *pb4 = new B---------
general construct B
---------B *pb5 = new B()---------
general construct B
---------B *pb6 = new B[3]---------
general construct B
general construct B
general construct B
---------B *pb7 = (B*)malloc(10)--------
---------B b8(5.0)------------------
general construct B double
--------------end------------------
destruct B: 0x75ecf0afd6af
destruct B: 0x75ecf0afd6b1
destruct B: 0x75ecf0afd6b0
destruct B: 0x75ecf0afd6ae
desctruct A: 0x75ecf0afd6d0
desctruct A: 0x75ecf0afd6c0
desctruct A: 0x601e14
从上面代码以及输出结果中,可以看出
1. 全局对象在进入main函数之前被创建。
g_a构造函数打印信息早于main函数内的打印信息
2. 调用含参构造函数的三种写法
A a2(2); // 直接构造
A a3 = A(3); // 在等号的右边创建:
A* pa4 = new A(4); // 通过new创建;
3. 调用含参构造函数的三种错误写法。
构造函数有参数时,不写参数就无法创建对象,下面的三种写法无法编译成功,原因在于他们指向的构造函数是A::A().
A a1;
A* pa5 = new A[3];
A a6[3];
4. new会调用构造函数,而malloc不会。
// A* pa5 = new A[3]; // 匹配A::A()
A* pa7 = (A*)malloc(2);
B *pb6 = new B[3];
B pb7 = (B)malloc(10);
对比上面四行代码的输出,可以知道new会去调用相应的构造函数,而malloc不会。因此pa5调用失败,因为找不到对应的构造函数,pa7与pb7调用后无信息打印,因为malloc仅仅只是申请了空间。
5. 调用无参构造函数六种情况。
B b1; // 无参构造,不写括号
b1 = B(); // 在等号左边构造
B b3[2]; // 在栈上创建数组
B* pb4 = new B; // 通过new创建,无括号
B* pb5 = new B(); // 通过new创建,有括号
B* pb6 = new B[3]; // 通过new在堆上创建
但是B b2并未调用构造函数是我未想到的,为什么呢?
6. 函数退出后会调用析构函数,释放在栈上创建的对象。
查看A调用析构函数的次数,可以看到,通过new创建的对象还在,析构函数的个数仅仅与栈和数据区有关,因此我们需要手动delete在堆上创建的对象
7. 先创建的对象后析构
上面的代码中先创建了一批A对象,再创建了一批B的对象,而析构时,先调用B类对象的析构函数,再调用A类对象的析构函数。
最后,再写一小段代码研究一下析构是在return之前还是return之后调用
int g_number = 10;
class D {
public:
D() {}
~D() {
g_number = 5;
cout << "destruct D call" << endl;
}
};
int func()
{
D d;
g_number = 100;
return g_number;
}
int main()
{
int x = func();
cout << "x: " << x << endl;
return 0;
}
结果输出为
destruct D call
x: 100
8. 析构函数在return之后调用
根据上面代码的例子,
若是析构在return之前调用,则应该 x: 5
若是析构在return之后调用,则应该 x: 100