- 从程序设计的角度,对象只是变量,因此:
-
在栈上创建对象时,成员变量初始值为随机值
-
在堆上创建对象时,成员对象初始值为随机值
-
在静态存储区创建对象时,成员变量初始为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