问题
对象中成员变量的初始值是多少?
成员变量的初始值
#include <stdio.h>
class Test
{
private:
int i;
int j;
public:
int getI() { return i; }
int getJ() { return j; }
};
Test gt;
int main()
{
printf("gt.i = %d\n", gt.getI());
printf("gt.j = %d\n", gt.getJ());
Test t1;
printf("t1.i = %d\n", t1.getI());
printf("t1.j = %d\n", t1.getJ());
Test* pt = new Test;
printf("pt->i = %d\n", pt->getI());
printf("pt->j = %d\n", pt->getJ());
delete pt;
return 0;
}
程序运行结果如下图所示
gt 变量是存储在静态存储区的,t1 变量是存储在栈上的,pt 变量是存储在堆上的,存储静态存储区变量的值初始值为0,存储在栈和堆上变量的初始值为随机值
从程序设计的角度,对象只是变量,因此:
在栈上创建变量时,成员变量初始值为随机值
在堆上创建变量时,成员变量初始值为随机值
在静态存储区创建变量时,成员变量初始值为 0
构造函数
C++ 中可以定义与类名相同的特殊成员函数
这种特殊的成员函数叫做构造函数
构造没有任何返回类型的声明
构造函数在对象定义时自动被调用
构造函数初探
#include <stdio.h>
class Test
{
private:
int i;
int j;
public:
int getI() { return i; }
int getJ() { return j; }
Test()
{
printf("Test() Begin\n");
i = 1;
j = 2;
printf("Test() End\n");
}
};
Test gt;
int main()
{
printf("gt.i = %d\n", gt.getI());
printf("gt.j = %d\n", gt.getJ());
Test t1;
printf("t1.i = %d\n", t1.getI());
printf("t1.j = %d\n", t1.getJ());
Test* pt = new Test;
printf("pt->i = %d\n", pt->getI());
printf("pt->j = %d\n", pt->getJ());
delete pt;
return 0;
}
代码运行结果如下图所示
我们创建了 3 个 Test 对象,通过打印可以看出 Test 构造函数被调用了三次;每创建一个类对象,这个类的构造函数就会被自动调用
带有参数的构造函数
构造函数可以根据需要定义参数
一个类中可以存在多个重载的构造函数
构造函数的重载遵循 C++ 重载的规则
对象定义和对象声明不同
对象定义:申请对象的空间,并调用构造函数
对象声明:告诉编译器存在这样一个对象
构造函数的自动调用
带参数的构造函数
#include <stdio.h>
class Test
{
public:
Test()
{
printf("Test()\n");
}
Test(int v)
{
printf("Test(int v), v = %d\n", v);
}
};
int main()
{
Test t; // 调用 Test()
Test t1(1); // 调用 Test(int v)
Test t2 = 2; // 调用 Test(int v)
int i(100);
printf("i = %d\n", i);
return 0;
}
程序运行结果如下图所示
第 18 行调用的是不带参数的构造函数,第 19 20 行,调用的是带参数的构造函数
构造函数的调用
一般情况下,构造函数在对象定义时被自动调用
一些特殊情况下,需要手工调用构造函数
需求:开发一个数组类解决原生数组的安全性问题
提供函数获取数组长度
提供函数获取数组元素
提供函数设置数组元素
两个特殊的构造函数
无参构造函数
- 没有参数的构造函数
- 当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空
拷贝构造函数
- 参数为 const class_name& 的构造函数
- 当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制
拷贝构造函数的意义
兼容 C 语言的初始化方式
初始化行为能够符合预期的逻辑
深拷贝
- 拷贝后对象的物理状态相同
浅拷贝
- 拷贝后对象的逻辑状态相同
编译器提供的拷贝构造函数只进行浅拷贝!
什么时候需要进行深拷贝?
对象中有成员指代了系统中的资源
成员指向了动态内存空间
成员打开了外存中的文件
成员使用了系统中的网络端口
问题分析
一般性原则
自定义拷贝构造函数,必然需要实现深拷贝!!!
小结
每个对象在使用之前都应该初始化
类的构造函数用于对象的初始化
构造函数与类同名并且没有返回值
构造函数在对象定义时自动被调用
构造函数可以根据需要定义参数
构造函数之间可以存在重载关系
构造函数遵循 C++ 中重载函数的规则
在一些情况下可以手动调用构造函数
C++ 编译器会默认提供构造函数
无参构造函数用于定义对象的默认初始化状态
拷贝构造函数在创建对象时拷贝对象的状态
对象的拷贝有浅拷贝和深拷贝两种
- 浅拷贝使得对象的逻辑状态相同
- 深拷贝使得对象的物理状态相同