对象的初始化和清理
构造函数–初始化
析构函数–清理
编译器自动调用,创建对象时,编译器自动调用构造函数,对象销毁时,编译器自动调用析构函数
默认情况下,C++会给一个类默认添加三个函数:
- 默认构造函数,函数体为空
- 默认析构函数,函数体为空
- 默认复制构造函数,对属性进行值拷贝
1 构造函数
class Point
{
private:
int p_x,p_y;
public:
Point(int x, int y):p_x(x),p_y(y) //自定义构造函数,初始化列表
{
}
};
语法:
- 没有返回值,并且不写void
- 函数名和类名相同
- 构造函数只在创建时调用一次,且无需手动调用
- 构造函数可以有参数,可以重载
特别注意:
- 如果程序员没有自定义构造函数,编译器会调用默认该构造函数,即为空函数
- 一旦程序员自定义了构造函数,C++不再提供无参默认构造函数,但还是会提供默认复制构造函数
- 一旦程序员自定义了复制构造函数,C++不再提供普通构造函数
调用:
Point p1(0,9);//方式一
Point p2 = Point(0,9);//方式二
Point p3 = (0,9); //方式三
构造函数的分类:
按照有无参数:有参构造函数和无参构造函数
按照类型分类:普通构造函数和复制构造函数
1.1 有参构造函数和无参构造函数
无参构造函数也称作默认构造函数
有参构造函数一般用作成员变量初始化工作
1.2 普通构造函数和复制构造函数
复制构造函数
实现:
Point(const Point &p)
{
p_x = p.p_x;
p_y = p.p_y;
}
除复制构造函数之外的都称作普通构造函数
调用:
Point p1(0,9);
Point p2(p1); //p2从p1中复制其成员变量进行初始化赋值
Point p3 = Point(p1);//方式二
Point p4 = p3;//方式三,
Point(10);//匿名对象,特点:当前行执行结束后,系统会立即回收
以下三种情况下会调用复制构造函数:
- 使用一个已创建的对象来初始化一个新对象
- 值传递的形式给函数形参传值
- 值的方式返回局部变量,即函数返回值为一个对象
浅拷贝和深拷贝:
浅拷贝:简单的赋值拷贝操作
深拷贝:在堆区重新申请空间,进行拷贝操作
下面举个例子来说明浅拷贝带来的问题,以下为出错代码:
#include <iostream>
using namespace std;
class Point
{
public:
int p_x,p_y;
int *p_z;
Point()
{
cout<<"Point的无参默认构造函数调用"<<endl;
}
Point(int x, int y, int z):p_x(x),p_y(y)
{
p_z = new int(z);
cout<<"Point的有参构造函数调用"<<endl;
}
~Point()
{
if(p_z) //当成员变量中有指针是,对象消亡时调用析构函数,释放堆区该指针空间
{
delete p_z;
p_z = NULL;
}
cout<<"Point的析构函数调用"<<endl;
}
};
void func01()
{
Point p1(10,0,2);
cout<<"("<<p1.p_x<<","<<p1.p_y<<","<<*(p1.p_z)<<")"<<endl;
/*若调用编译器提供的默认拷贝构造函数会做一个浅拷贝,即p2的p_z直接拷贝的p1的 p_z,即指向同一内存空间
当p2消亡时,析构函数第一次调用时已经释放p_z空间,
后p1消亡时,再次调用析构函数,会造成堆区内存重复释放 */
Point p2(p1);
cout<<"("<<p2.p_x<<","<<p2.p_y<<","<<*(p2.p_z)<<")"<<endl;
}
int main()
{
func01();
system("pause");
return 0;
}
解决方案:
用深拷贝解决浅拷贝的问题,自定义复制构造函数
Point(const Point &p)
{
cout<<"Point的复制构造函数调用"<<endl;
p_x = p.p_x; p_y = p.p_y;
p_z = new int(*p.p_z);
}
输出:
Point的有参构造函数调用
(10,0,2)
Point的复制构造函数调用
(10,0,2)
Point的析构函数调用
Point的析构函数调用
2 析构函数
语法:
- 没有返回值,并且不写void
- 函数名和类名相同,在函数名前加"~"
- 析构函数只在对象销毁时调用一次,且无需手动调用
- 析构函数没有参数,不可以重载
- 如果程序员没有自定义析构函数,编译器会调用默认该析构函数,即为空函数
类对象作为类成员
class Circle
{
Point p;//Point是另一个已声明的类
int c_r;
}
当创建一个Circle对象时,构造函数和析构函数的调用顺序:
- 调用Point构造函数
- 调用Circle构造函数
- 调用Circle析构函数
- 调用Point析构函数
构造的顺序是先构造成员对象的构造,再构造自身,析构的顺序与之相反
3 静态成员
在类成员前加上static关键字,即称为静态成员,静态成员分为静态成员变量和静态成员函数
静态成员变量:
- 为所有对象所公有
- 在编译阶段分配内存
- 类内声明,类外初始化
静态成员函数:
- 只能访问静态成员,静态成员函数类似全局函数
- 为所有对象所公有
类外不能访问私有静态成员函数。
调用:
//1.通过对象进行访问
Point p;
p.func();//func是一个静态成员函数
//2.通过类名进行访问
Point::func();