构造函数与析构函数
1类的构造函数
引例
定义一个类型:Circle, 圆
成员:圆心坐标(x,y),半径radius
class Circle
{
public:
int x, y;
int radius;
};
引例
定义一个对象
Circle a;
// 问题: 此时这个对象a算是一个有效的对象吗?
// 看看它的x,y,和radius,是乱的
那么再给它赋值不就好了吗?
a.x=0;
a.y=0;
a.radius = 1;
但是从“对象”的观点来看,对象被创建之时,就应该是有效的,不应该存在“半 成品”对象。
比如说,一辆车Car在造出来的时候,必须带着轮子。如果没有轮子,此时它还不能 称为Car。
引例
如何让一个对象在被创建的时候,就赋予初始值。
方法是:构造函数
构造函数
构造函数是类的一种特殊的成员函数:
(1) 函数名与类名必须相同
(2) 没有返回值
例如,
class Circle
{
…
public:
Circle ()
{
x = y = 0;
radius = 1;
}
};
构造函数
构造函数可以带参数,也可以重载
class Circle
{
…
public:
Circle ()
{
x = y = 0;
radius = 1;
}
Circle(int x, int y, int r)
{
this->x = x;
this->y = y;
this->radius = r;
}
};
构造函数如何调用
构造函数和普通成员函数不一样,一般不显式调用。
在创建一个对象时,构造函数被自动调用。(由编译器完成)
例如,
Circle a;
Circle b(1,1, 4);
它们在内部实质上是分别调用了不同的构造函数,但是表面 上没有这个函数调用过程。
构造函数如何调用
构造函数的作用:对象一“出生”就是有效的。不存在“半 成品”对象。
它可以理解为“初始化”动作。
基本类型的初始化:
int a(10); // 将a初始化为10, 也可以写成int a = 10;
Student s={1,“shaofa”}; // struct的初始化
现在,类class的初始化使用构造函数的方式。
小结
- 介绍构造函数的语法:名字与类名相同,没有返回值
- 构造函数的作用:用于初始化对象
- 构造函数的调用:创建对象的同时,被编译器自动调用。
- 构造函数也可以重载。
2类的析构函数
析构函数
析构和构造是一对相反的过程。
构造函数:对象被创建时补调用
析构函数:对象被销毁时被调用
析:分崩离析
英文:构造函数 constructor 析构函数destructor
析构函数
析构函数也不是普通的函数
(1) 名称固定:类名前加上波浪线~
(2) 没有返回值
(3) 不能带参数
例如, class Object
{
public:
~Object()
{
}
};
注:析构函数只能有一个,不允许重载
析构函数如何调用?
析构函数从不显式地调用,而是被编译器自动地调用。
什么时候被调用?对象被销毁之时
。
对于局部变量(对象),在超出变量作用域后,该对象失效 (关于局部变量的作用域,在第8章)
析构函数如何调用?
int main()
{
{
Object obj;
}
// obj失效 return 0; }
析构函数如何调用?
void test()
{
Object obj;
}
// obj失效
int main()
{
test();
return 0;
}
析构函数的作用
析构函数:对象在销毁之前,做一个清理善后工作。
比如,申请来的内存要释放掉,打开的文件FILE*要关闭 掉。。
析构函数的作用
例如,
class StringBuffer
{
public:
StringBuffer()
{
m_buffer = malloc(1024*512); // 申请内存
}
~StringBuffer()
{
free(m_buffer); // 释放内存
}
};
析构函数的作用
例如,
class DataStore
{
public:
DataStore()
{
m_head.next = NULL;
}
~DataStore()
{
// 删除链表中所有节点,把所有malloc的内存都free掉
}
private:
Student m_head;
};
.h/.cpp 分离式写法
分开写在.h和.cpp里时,规则和普通函数一样
class DataStore
{
public:
DataStore(); // 函数声明
~DataStore();
};
DataStore::DataStore() // 函数定义,要加类名前缀
{
}
DataStore:: ~DataStore()
{
}
3再说构造与析构
内容提要
- 默认构造函数
- 成员的构造与析构顺序
- 成员的初始化列表
默认构造函数
把那种不需要传参的构造函数
,称为默认构造函数。
例如,
Object();
或者构造函数的所有参数都有缺省值,也称为默认构造函数
Object(int a=10, int b=11);
(函数默认值的语法参考第8章)
有了默认构造函数之后,对象在构造时就可以不传递参数。
例如, Object obj;
默认构造函数
默认构造函数很重要。如果一个类没有默认构造函数,则无法构 造数组。
例如,
class Object
{
public:
Object(int x)
{
}
};
Object objs[4]; // 错误!
所以,一般都要给类定义一个默认构造参数,以方便使用。
没写构造函数?没写析构函数?
如果一个类没有写任何构造函数,则编译器隐含地生成为其添加 一个,相当于
// 相当于添加了
Object::Object()
{
}
如果没写析构函数,则编译器隐含地生成一个析构函数 相当于添加了
Object::~Object()
{
}
(注意:只有当你没有写构造函数的时候,才给隐含地加一个构 造函数。一旦你写了一个构造函数,它就不给你加了)
成员的初始化与析构
考虑成员变量本身也是 class类型的情况:
- 当对象被构造时,成员变量也被构造(成员变量的构造 函数被调用)
- 当对象被析构时,成员变量也被析构(成员变量的析构 函数被调用)
成员的初始化与析构
结论:
一、构造的时候
(1)成员被依次构造: 从前到后
(2)先执行成员的构造函数,再执行自己的构造函数
二、析构的时候
(1)成员被依次析构: 从后到前
(2)先执行自己的析构函数,再执行成员的析构
初始化列表
可以在构造函数后面直接初始化
(1)以冒号引导
(2)使用小括号来初始化
初始化列表
(1)基本类型的初始化
class Object
{
public:
Object() : x(1), y(2)
{
}
};
初始化列表
(2) class类型的初始化
用小括号带上构造参数
class Object
{
public:
Object() : m_child(1,2)
{
}
};
初始化列表
"初始化"与"赋值"有何区别?
(1)功能上:一样
(2)性能上:"初始化"相对较好,有时可以节省CPU操作
class Object
{
public:
Object() // 已经对 m_child进行了构造
{
m_child.x = 1;
m_child.y = 2; // 又多了一步操作
}
};
小结
- 默认构造函数:不需要传参的构造函数 这个非常必要,一般的类都要提供一下默认构造函数
- 构造与析构的顺序问题
- 初始化列表 在构造函数的位置指定成员的初始化构造参数