目录
前言:
构造函数的显示调用
显示调用无参构造
隐式调用无参构造
显示调用有参构造
构造函数的执行顺序
析构函数的显示调用
析构函数的调用顺序
显示调用析构函数
前言:
构造函数是类的特殊成员函数,创建对象时编译器会自动调用构造函数;
析构函数是类的特殊成员函数,当对象的生命周期结束时,编译器自动隐式调用析构函数;
构造函数的显示调用
构造函数按照参数类型可以分为有参构造与无参构造,调用构造函数的方式分别为显示调用与隐式调用;
显示调用无参构造
class Date
{
public:
//构造函数函数名与类名相同且无返回值
//无参构造
Date()
{
cout << "调用构造函数" << endl;
}
private:
int _year = 2024;
int _month = 3;
int _day = 1;
};
int main()
{
//显示调用
Date d = Date();
return 0;
}
监视窗口:
运行窗口:
注:构造函数调用时先执行初始化列表的位置,在执行函数体中的语句;
隐式调用无参构造
class Date
{
public:
//构造函数函数名与类名相同且无返回值
//无参构造
Date()
{
cout << "调用构造函数" << endl;
}
private:
int _year = 2024;
int _month = 3;
int _day = 1;
};
int main()
{
//隐式调用
Date d;
return 0;
}
监视窗口:
运行窗口:
显示调用有参构造
class Date
{
public:
//构造函数函数名与类名相同且无返回值
//有参构造
Date(int year,int month,int day)
: _year(year)
, _month(month)
, _day(day)
{
cout << "调用构造函数" << endl;
}
private:
int _year = 2024;
int _month = 3;
int _day = 1;
};
int main()
{
//显式调用
Date d(2024, 3, 15);
return 0;
}
监视窗口:
运行窗口:
总结:
- 一般情况下默认的无参构造函数是自动地隐式调用;
- 有参构造函数是由开发者手动地显示调用;
构造函数的执行顺序
class A
{
public:
A()
{
cout << "调用A类的构造函数" << endl;
}
private:
int _a = 1;
};
class B
{
public:
B()
{
cout << "调用B类的构造函数" << endl;
}
private:
int _b = 2;
};
class C
{
public:
C()
{
cout << "调用C类的构造函数" << endl;
}
private:
int _c = 3;
};
class D
{
public:
D()
{
cout << "调用D类的构造函数" << endl;
}
private:
int _d = 4;
};
C c;//全局对象
int main()
{
A a;
B b;
static D d;//静态对象
return 0;
}
运行结果:
构造函数:定义对象时进行调用;
析构函数的显示调用
析构函数的调用顺序
class A
{
public:
~A()
{
cout << "调用A类的析构函数" << endl;
}
private:
int _a = 1;
};
class B
{
public:
~B()
{
cout << "调用B类的析构函数" << endl;
}
private:
int _b = 2;
};
class C
{
public:
~C()
{
cout << "调用C类的析构函数" << endl;
}
private:
int _c = 3;
};
class D
{
public:
~D()
{
cout << "调用D类的析构函数" << endl;
}
private:
int _d = 4;
};
C c;//全局对象
int main()
{
A a;//局部对象
B b;//局部对象
static D d;//静态对象
return 0;
}
运行结果:
监视窗口:
- 局部对象的析构顺序与构造顺序相反,由于局部对象存放于栈区,栈区高地址处存放对象a,低地址处存放对象b(栈区使用习惯先使用高地址,再使用低地址),析构时先销毁低地址处的局部对象,后销毁高地址处的局部对象;
- static修饰的静态对象存放于静态区,对象生存的作用域(main()函数内部)结束时销毁;
- 程序结束时销毁全局对象;
显示调用析构函数
class Test
{
public:
Test()
{
cout << "调用构造函数" << endl;
}
~Test()
{
cout << "调用析构函数" << endl;
}
private:
int _a;
};
int main()
{
Test t;
t.~Test();//显示调用析构函数
return 0;
}
运行结果:
开发者创建的对象通常在语句体{ }中,当语句体{ }结束时该对象被销毁,但是这种对象通常存放于栈区这就意味着如何管理此对象,是由操作系统完成而开发者无法控制;所以即使开发者显示调用析构函数,当对象的生命周期结束时,操作系统依然会再次调用析构函数,将其在栈区销毁,实现真正的析构;
class Test
{
public:
Test()
{
cout << "调用构造函数" << endl;
_a = new int[10];//堆区申请空间
}
~Test()
{
cout << "调用析构函数" << endl;
delete[] _a;//释放堆区开辟的空间
}
private:
int* _a;
};
int main()
{
Test t;
t.~Test();
return 0;
}
运行结果:
一旦析构函数中存在释放堆空间的语句,第二次调用析构函数会释放已经释放的空间,导致系统崩溃;
第一次显示调用析构函数,相当于调用一个普通的成员函数,执行函数体内的语句,释放了堆内存,但是并未释放栈内存,对象还存在;
第二次操作系统调用析构函数,再次释放堆内存,造成系统崩溃,然后真正释放栈内存,销毁对象;