两个特殊的构造函数无参构造函数没有参数的构造函数拷贝构造函数参数为 const class_name& 的构造函数无参构造函数当类中没有定义构造函数时,编译器提供一个无参构造函数,并且函数体为空拷贝构造函数当类中没有定义拷贝构造函数时,编译器默认提供一个拷贝构造函数,简单的进行成员变量的值复制编程实验: 特殊的构造函数Test_1.cpp#include <stdio.h>class Test{private: int i; int j;public: int getI() { return i; } int getJ() { return j; }};int main(){ Test t1; Test t2; 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;}第一次输出:t1.i = 134514464, t1.j = -1081276856t2.i = 2528036, t2.j = 2527220第二次输出:t1.i = 134514464, t1.j = -1081626328t2.i = 3298084, t2.j = 3297268第三次输出:t1.i = 134514464, t1.j = -1080045768t2.i = 9716516, t2.j = 9715700分析:类中没有定义构造函数,编译器提供一个无参构造函数,函数体为空,无法对成员变量初始值。打印栈空间中的随机值。编译器在类中未发现构造函数,会在类最后添加无参构造函数:Test(){}test_2.cpp#include <stdio.h>class Test{private: int i; int j;public: int getI() { return i; } int getJ() { return j; }};int main(){ Test t1; 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;}第一次输出:t1.i = 134514480, t1.j = -1081214552t2.i = 134514480, t2.j = -1081214552第二次输出:t1.i = 134514480, t1.j = -1082006488t2.i = 134514480, t2.j = -1082006488第三次输出:t1.i = 134514480, t1.j = -1077369784t2.i = 134514480, t2.j = -1077369784分析:类中没有定义拷贝构造函数,编译器提供一个拷贝构造函数,简单的进行成员变量的复制。t1.i == t2.it1.i == t2.i编译器在类中未发现拷贝构造函数,会在类最后添加简单的拷贝构造函数:Test(const Test& t){ i = t.i; j = t.j;}class Test{private: int i; int j;public: int getI() { return i; } int getJ() { return j; }}; <==>class Test{private: int i; int j;public: int getI() { return i; } int getJ() { return j; } Test() // 无参构造函数,函数体为空 { } Test(const Test& t) // 拷贝构造函数,进行简单的成员变量复制 { i = t.i; j = t.j; }};经典的面试问题class T{};问: T 中包含什么?答: 无参构造函数与拷贝构造函数。拷贝构造函数拷贝构造函数的意义兼容 C 语言的初始化方式初始化行为符合预期的逻辑浅拷贝拷贝后对象的物理状态相同(两对象占用的内存空间中,每个字节的值相等)深拷贝拷贝后对象的逻辑状态相同编译器提供的拷贝构造函数只进行浅拷贝!编程实验: 对象的初始化浅拷贝的现象:test_1.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(int v) { i = 1; j = 2; p = new int; *p = v; } void free() { delete p; }};int main(){ Test t1(3); Test t2 = t1; // ==> Test t2(t1); printf(“t1.i = %d, t1.j = %d, t1.p = %p, *t1.p = %d\n”, t1.getI(), t1.getJ(), t1.getP(), *t1.getP()); printf(“t2.i = %d, t2.j = %d, t2.p = %p, *t2.p = %d\n”, t2.getI(), t2.getJ(), t2.getP(), *t2.getP()); return 0;}输出:t1.i = 1, t1.j = 2, t1.p = 0x950c008, t1.p = 3t2.i = 1, t2.j = 2, t2.p = 0x950c008, t2.p = 3分析:类中没有定义拷贝构造函数,编译器提供一个拷贝构造函数,简单的进行成员变量的复制。t1.p = 0x950c008,t2.p = 0x950c008 指向同一内存空间,而没有为 t2.p 重新分配空间,不是期望结果。浅拷贝的问题: test_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(int v) { i = 1; j = 2; p = new int; *p = v; } void free() { delete p; }};int main(){ Test t1(3); Test t2 = t1; // ==> Test t2(t1); printf(“t1.i = %d, t1.j = %d, t1.p = %p, *t1.p = %d\n”, t1.getI(), t1.getJ(), t1.getP(), *t1.getP()); printf(“t2.i = %d, t2.j = %d, t2.p = %p, t2.p = %d\n”, t2.getI(), t2.getJ(), t2.getP(), t2.getP()); t1.free(); // 注意这里! t2.free(); // 注意这里! return 0;}输出:内存错误深拷贝实例: test_3.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(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; // ==> Test t2(t1); printf(“t1.i = %d, t1.j = %d, t1.p = %p, *t1.p = %d\n”, t1.getI(), t1.getJ(), t1.getP(), *t1.getP()); printf(“t2.i = %d, t2.j = %d, t2.p = %p, *t2.p = %d\n”, t2.getI(), t2.getJ(), t2.getP(), *t2.getP()); t1.free(); t2.free(); return 0;}输出:t1.i = 1, t1.j = 2, t1.p = 0x927b008, *t1.p = 3t2.i = 1, t2.j = 2, t2.p = 0x927b018, t2.p = 3分析:t1.p = 0x927b008, t2.p = 0x927b018 指向不同的内存空间。指向的内存空间中的值相同。什么时候需要进行深拷贝?对象中有成员指代了系统中的资源成员指向了动态内存空间成员打开了外存中的文件成员使用了系统中的网络端口。。。。。问题分析浅拷贝只进行简单的成员变量值复制,所以发生 t2.m_pointer=t1.m_pointer; 两对象成员变量指向同一片内存空间而没有发生新的内存申请,在 free 时,对同一片内存空间释放两次,导致运行时内存出错。一般原则自定义拷贝构造函数,必须需要实现深拷贝!!编程实验:数组类的改进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); int length(); bool get(int index, int& value); bool set(int index, int value); void free();};#endifIntArray.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]; }}int IntArray::length(){ return m_length;}bool IntArray::get(int index, int& value){ bool ret = (index >= 0) && (index < length()); if( ret ) { value = m_pointer[index]; } return ret;}bool IntArray::set(int index, int value){ bool ret = (index >= 0) && (index < length()); if( ret ) { m_pointer[index] = value; } return ret;}void IntArray::free(){ delete[] m_pointer;}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); } } a.free(); b.free(); return 0;}输出:a.[0] = 1a.[1] = 2a.[2] = 3a.[3] = 4a.[4] = 5b.[0] = 1b.[1] = 2b.[2] = 3b.[3] = 4b.[4] = 5小结C++ 编译器会默认提供构造函数无参构造函数用于定义对象的默认初始状态拷贝构造函数在创建对象时拷贝对象的状态对象的拷贝有浅拷贝和深拷贝两种方式浅拷贝使得对象的物理状态相同深拷贝使得对象的逻辑状态相同以上内容参考狄泰软件学院系列课程,请大家保护原创!