第19课 对象的构造(下)
1. 两个特殊的构造函数
无参构造函数 |
拷贝构造函数 |
|
参数形式 |
没有参数的构造函数 |
参数为const class_name&的构造函数 |
默认情况 |
当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空 |
当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制 |
注意: |
当类中定义了构造函数(含带参、不带参、或拷贝构造函数),则系统就不再提供默认的无参构造函数。而拷贝构造函数只有在我们定义时,系统才不提供。 |
【编程实验】特殊的构造函数(无参构造函数和拷贝构造函数) 19-1.cpp
#include <stdio.h> class Test { private: int i; int j; public: int getI(){return i;} int getJ(){return j;} /* //拷贝构造函数 Test(const Test& t) { i = t.i; j = t.j; } */ //无参构造函数 Test() { } }; int main() { //调用无参构造函数,注意如果我们定义了构造函数(含无参、带参或拷贝 //构造函数时)系统就不再提供默认的,需自己定义无参构造函数。 Test t1; //调用Test() Test t2 = t1; //调用拷贝构造函数,如果我们不定义,系统会提供默认的 printf("t1.i = %d, t1.j = %d\n",t1.getI(),t1.getJ()); printf("t2.i = %d, t2.j = %d\n",t2.getI(),t2.getJ()); return 0; }
运行结果:
2. 拷贝构造函数
(1)拷贝构造函数的意义
①兼容C语言的初始化方式,即利用己经存在的对象去创建新的对象。(因为C++中初始化会涉及到拷贝构造函数的调用。注意初始化与赋值是不同的,赋值时“=”运算符会被调用)
如:int a = b; //C中,用一个变量来初始化另一个变量;
Student s2 = s1;//利用己经存在的s1对象来初始化,很像C的初始化方式
②初始化行为能够符合预期的逻辑
(2)浅拷贝和深拷贝
①拷贝后对象的物理状态相同------->编译器提供的拷贝构造函数只进行浅拷贝
②拷贝后对象的逻辑状态相同
(3)什么时候需要进行深拷贝
-
对象中有成员指代了系统中的资源,如:成员指向了动态内存空间、打开了外存中的文件或使用了系统中的网络端口等
-
一般性原则:自定义拷贝构造函数时,必然需要实现深拷贝
深拷贝和浅拷贝的区别
1.浅拷贝: 将原对象或原数组的引用直接赋给新对象,新数组。新对象、新数组只是原对象的一个引用
2.深拷贝: 创建一个新的对象和数组,将原对象的各项属性的“值”(数组的所有元素)拷贝过来,是“值”而不是“引用”
为什么要使用深拷贝?
我们希望在改变新的数组(对象)的时候,不改变原数组(对象)
深拷贝的要求程度:
我们在使用深拷贝的时候,一定要弄清楚我们对深拷贝的要求程度:是仅“深”拷贝第一层级的对象属性或数组元素,还是递归拷贝所有层级的对象属性和数组元素?
怎么检验深拷贝成功:
改变任意一个新对象/数组中的属性/元素,都不改变原对象/数组
【编程实验】对象的初始化 19-2.cpp
#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(const Test& t) { i = t.i; j = t.j; p = new int; *p = *t.p; } */ //带参构造函数 Test(int v) { i = 1; j = 2; p = new int; *p = v; } ~Test(){delete p;} }; int main() { Test t1(3); //调用Test(int v); Test t2(t1); //调用Test(const Test& t)---浅拷贝 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; }
运行结果:
【编程实验】数组类的改进 IntArray
//IntArray.h
#ifndef _INTARRAY_H_ #define _INTARRAY_H_ class IntArray { private: int m_length; int* m_pointer; public: IntArray(int len); IntArray(const IntArray& obj); ~IntArray(); int length(); bool get(int index, int& value); bool set(int index, int value); }; #endif
//IntArray.cpp
#include "IntArray.h" IntArray::IntArray(int len) { m_pointer = new int[len]; for(int i = 0; i<len; i++) { m_pointer[i] = 0; } m_length = len; } IntArray::IntArray(const IntArray& obj) { m_length = obj.m_length; m_pointer = new int[obj.m_length]; for (int i = 0;i < obj.m_length; i++) { m_pointer[i] = obj.m_pointer[i]; } } IntArray::~IntArray() { if(m_pointer) { delete[] m_pointer; } } int IntArray::length() { return m_length; } bool IntArray::get(int index, int& value) { bool bRet = (0 <= index) && (index <m_length); if(bRet) { value = m_pointer[index]; } return bRet; } bool IntArray::set(int index, int value) { bool bRet = (0 <= index) && (index <m_length); if(bRet) { m_pointer[index] = value; } return bRet; }
//main.cpp
#include <stdio.h> #include "IntArray.h" int main() { IntArray a(5);//调用带参构造函数 for(int i=0; i<a.length(); i++) { a.set(i, i + 1); } for(int i=0; i<a.length(); i++) { int value = 0; if(a.get(i, value)) { printf("a[%d] = %d\n", i, value); } } IntArray b = a; //调用拷贝构造函数 for(int i=0; i<b.length();i++) { int value = 0; if(b.get(i, value)) { printf("b[%d] = %d\n", i, value); } } return 0; }
3. 小结
(1)C++编译器会默认提供构造函数
(2)无参构造函数用于定义对象的默认初始状态
(3)拷贝构造函数在创建对象时拷贝对象的状态
(4)对象的拷贝有浅拷贝和深拷贝两种方式
①浅拷贝使得对象的物理状态相同------->编译器提供的拷贝构造函数只进行浅拷贝
②深拷贝使得对象的逻辑状态相同