- 从程序设计的角度,对象只是变量,因此:
-
在栈上创建对象时,成员变量初始值为随机值
-
在堆上创建对象时,成员对象初始值为随机值
-
在静态存储区创建对象时,成员变量初始为0值
- 一般而言,对象都需要一个确定的初始状态
- 解决方案:在类中提供一个public的initialize函数,对象创建后立即调用initialize函数进行初始化
class Test
{
private:
int i;
int j;
public:
void initialize(){i = 0;j = 0;}
int getI(){return i;}
int getJ(){return j;}
};
- 存在的问题
- initialize只是一个普通的函数,必须显示调用
- 如果未调用initialize函数,运行的结果是不确定的
- C++中可以定义与类名相同的特殊成员函数
-
这种特殊的成员函数叫做构造函数
- 构造没有任何返回类型的声明
- 构造函数在对象定义时自动被调用
1 #include <stdio.h>
2 class Test
3 {
4 private:
5 int i;
6 int j;
7 public:
8 int getI(){ return i; }
9 int getJ(){ return j; }
10 //构造函数
11 Test()
12 {
13 i = 1;
14 j = 2;
15 }
16 };
17 Test a;
18 int main()
19 {
20 printf("a.i=%d\n",a.getI());
21 printf("a.j=%d\n", a.getJ());
22 Test b;
23 printf("b.i=%d\n", b.getI());
24 printf("b.j=%d\n", b.getJ());
25 Test *pt = new Test;
26 printf("pt->i=%d\n",pt->getI());
27 printf("pt->j=%d\n",pt->getJ());
28 delete pt;
29 return 0;
30 }
31
32 运行结果:
33 a.i=1
34 a.j=2
35 b.i=1
36 b.j=2
37 pt->i=1
38 pt->j=2
39 请按任意键继续. . .
-
带有参数的构造函数
-
构造函数可以根据需要定义参数
- 一个类中可以存在多个重载的构造函数
-
构造函数的重载规则遵循C++的重载规则
1 class Test
2 {
3 public:
4 //带参构造函数
5 Test(int v)
6 {
7 //use v to initialize member
8 }
9 };
- 友情提示
- 对象定义和对象声明不同
- 对象定义:申请对象的空间并调用构造函数
- 对象声明:告诉编译器存在这样一个对象
1 //定义对象并调用构造构造函数
2 Test t;
3 int main
4 {
5 //告诉编译器存在名为t的Test对象
6 extern Test t;
7 return 0;
8 }
- 构造函数的自动调用
1 #include <stdio.h>
2 class Test
3 {
4 public:
5 Test()
6 {
7 printf("Test()\n");
8 }
9 Test(int v)
10 {
11 printf("Test(int v),v=%d\n",v);
12 }
13 };
14 int main()
15 {
16 Test t; // 调用Test()
17 Test t1(1); // 调用Test(int v)
18 Test t2 = 2; // 调用Test(int v)
19 t = t2; //赋值操作,初始化才会调用构造函数
20 int i(100); //初始化,(初始化和赋值是两个概念,不等价)
21 printf("i=%d\n",i);
22 return 0;
23 }
24 运行结果:
25 Test()
26 Test(int v),v=1
27 Test(int v),v=2
28 i=100
29 请按任意键继续. . .
- 构造函数的调用
- 一般情况下,构造函数在对象定义时被自动调用
- 一些特殊情况下,需要手工调用构造函数
- 如何创建一个对象数组???
1 #include <stdio.h>
2 class Test
3 {
4 public:
5 Test()
6 {
7 printf("Test()\n");
8 }
9 Test(int v)
10 {
11 printf("Test(int v),v=%d\n",v);
12 }
13 };
14 int main()
15 {
16 Test t; // 调用Test()
17 Test t1(1); // 调用Test(int v)
18 Test t2 = 2; // 调用Test(int v)
19 t = t2; //赋值操作,初始化才会调用构造函数
20 int i(100); //初始化,(初始化和赋值是两个概念,不等价)
21 printf("i=%d\n",i);
22 Test Ta[3] = {Test(),Test(1),Test(2)}; //手动调用构造函数
23 Test ta = Test(3);
24 return 0;
25 }
26
- 两个特殊的构造函数
- 无参构造函数:没有参数的构造函数,当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空。
- 拷贝构造函数:参数为const class_name&的构造函数,当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制。
1 #include <stdio.h>
2 class Test
3 {
4 private:
5 int i;
6 int j;
7 public:
8 int getI()
9 {
10 return i;
11 }
12 int getJ()
13 {
14 return j;
15 }
16 //编译器会在没有提供一个构造函数时,才会提供无参构造函数
17 Test()
18 {
19 }
20 //拷贝构造函数,当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制。
21 Test(const Test&t)
22 {
23 i = t.i;
24 j = t.j;
25 }
26 };
27 int main()
28 {
29 Test T1;
30 Test T2 = T1;
31 printf("T1.I=%d,T1.J=%d\n",T1.getI(),T1.getJ());
32 printf("T2.I=%d,T2.J=%d\n", T2.getI(), T2.getJ());
33 return 0;
34 }
- 拷贝构造函数的意义
- 兼容C语言的初始化方式
- 初始化行为能够符合预期的逻辑(初始化会牵涉到拷贝构造函数的调用)
- 拷贝构造函数的意义
- 浅拷贝:拷贝后对象的物理状态相同
- 深拷贝:拷贝后对象的逻辑相同
-
编译器提供的拷贝构造函数只进行浅拷贝
#include <stdio.h>
class Test
{
private:
int i;
int j;
int *p;
public:
int getI()
{
return i;
}
int getJ()
{
return j;
}
int* getp()
{
return p;
}
//编译器会在没有提供一个构造函数时,才会提供无参构造函数
//Test()
//{
//}
Test(int v)
{
i = 1;
j = 2;
p = new int;
*p = v;
}
//拷贝构造函数,当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制。
Test(const Test&t)
{
i = t.i;
j = t.j;
p = new int;
*p = *t.p;
}
void free()
{
delete p;
}
};
int main()
{
Test T1(3);
Test T2(T1);
printf("T1.I=%d,T1.J=%d,*T1.p=%d\n",T1.getI(),T1.getJ(),*T1.getp());
printf("T2.I=%d,T2.J=%d,*T2.p=%d\n",T2.getI(), T2.getJ(),*T2.getp());
return 0;
}
- 什么时候进行深拷贝?
- 对象中有成员指代了系统中的资源
- 成员指向了动态内存空间
- 成员打开了外存中的文件
- 成员使用了系统中的网络端口
- 问题分析:
当没有进行深拷贝时,程序将默认进行浅拷贝,程序指向同一个内存空间
浅拷贝
深拷贝(手工提供构造函数)

- 一般性原则
- 自定义拷贝构造函数,必然需要实现拷贝构造函数
- 小结
- C++编译器会默认提供构造函数
- 无参构造函数用于定义对象的默认的初始状态
- 拷贝构造函数在创建对象时拷贝对象的状态
- 对象的拷贝有浅拷贝和深拷贝两种方式:浅拷贝使得对象的物理状态相同,深拷贝使得对象的逻辑状态相同。
来源:https://www.cnblogs.com/chengeputongren/p/12185375.html