目录
对象的赋值
目录
对象的赋值
1、提出问题:
2、解决办法:
拷贝构造函数
1、拷贝构造函数的原型:
2、调用机制:
3、使用例程代码
总代码工程:
对象的赋值
1、提出问题:
能否使用一个已经构造好的对象去初始化另一个对象,C++编译器又是如何处理这些操作的呐。
类似于:
A a(10);
A b = a;
先来运行一下下边这一段程序:
#include <iostream> using namespace std; class Test { public: int *sum; int x; int y; Test() { cout << "Test()" << endl; x = 0; y = 0; sum = new int[4]; } Test(int a, int b):x(a), y(b) { cout << "Test(int a, int b)" << endl; sum = new int[4]; } }; int main() { Test t1(10, 20); //Test t1 = (10, 20); t1.sum[0] = 1; t1.sum[1] = 2; t1.sum[2] = 3; t1.sum[3] = 4; Test t2 = t1; cout << t1.x << " " << t1.y << endl; cout << t2.x << " " << t2.y << endl; cout << t1.sum[2] << " " << t2.sum[2] << endl; cout << t1.sum << " " << t2.sum << endl; return 0; }
打印结果如上图所示,对象t1和t2不止成员变量的值相同 ,地址也相同。也就是类似于两个对象名称共用一个内存空间。
这样的坏处是当只改变t2的成员变量值的时候相应t1的成员变量也会改变。
如果以下边一种写法:
#include <iostream> using namespace std; class Test { public: int *sum; int x; int y; Test() { cout << "Test()" << endl; x = 0; y = 0; sum = new int[4]; } Test(int a, int b):x(a), y(b) { cout << "Test(int a, int b)" << endl; sum = new int[4]; } ~Test() { cout << "~Test" << endl; delete[] sum; } }; int main() { //通过new操作符 构造对象(申请空间) Test *t1 = new Test(10, 20); t1->sum[0] = 10; t1->sum[1] = 20; t1->sum[2] = 30; t1->sum[3] = 40; Test *t2 = t1; cout << t2->sum[1] << endl; delete t1; cout << t2->sum[1] << endl; //cout << t1->sum << " " << t2->sum << endl; return 0; }
发现:在执行delete t1语句前和执行后t2->sum[1]的值是不一样的,因为在t1->sum和t2->sum指向的是同一块内存空间当执行delete t1语句的时候会调用析构函数,在析构函数中之前申请的sum空间被释放了,所以s2->sum[1]的值就和原来的不一样了。
由上边两个问题得知:
用一个构造好的对象使用=可以构造一个新的对象,而且新的对象中的成员变量的值和构造好的对象中的成员变量的值是相等的(实现对象的拷贝)。但是这样做带来的危害是:如果两个类中有成员变量是指针类型(对于其他普通成员变量不会这样{第二种代码也会修改普通成员变量的值}),一旦其中一个对象被销毁调用析构函数将指针变量指向的堆空间进行了释放,另外一个对象所操作的指针所执行的堆空间是非法的!!(也就是后边要讲的浅拷贝)。
那我们想通过使用一个已经构造好的对象去初始化另一个对象而且对于指针类型成员变量不收影响该怎么办呐。
2、解决办法:
法子1:直接分开赋值
Test t1(10, 20); //Test t1 = (10, 20); t1.sum[0] = 1; t1.sum[1] = 2; t1.sum[2] = 3; t1.sum[3] = 4; Test t2; //普通成员变量的赋值 t2.x = t1.x; t2.y = t1.y; //指针类成员变量的赋值使用下面的memcpy函数 memcpy(t1.sum, t2.sum, 10*sizeof(int));
法子2:引出拷贝构造函数
依然使用“=”
原因:当我们使用“=”使用一个构造好的对象初始化一个新的对象的时候时,没有实现拷贝函数,编译器自动生成拷贝函数调用
拷贝构造函数
1、拷贝构造函数的原型:
类名(const 类名 &变量名);//此处变量名相当于一个形参的变量名。
2、调用机制:
使用一个构造好的对象初始化一个新的对象。
3、使用例程代码
如下:
#include <iostream> using namespace std; class Test { public: int *sum; int x; int y; Test() { cout << "Test()" << endl; x = 0; y = 0; sum = new int[4]; } Test(int a, int b):x(a), y(b) { cout << "Test(int a, int b)" << endl; sum = new int[4]; } Test(const Test &t) //拷贝构造函数 { cout << "Test(const Test &t)" << endl; //将t对象中的成员变量的值拷贝给新的对象 x = t.x; y = t.y; sum = new int[4];//重新另给sum分配空间 memcpy(sum, t.sum, 4*sizeof(int));//将数据拷贝过去 /* for(int i=0;i<4;i++) sum[i] = t.sum[i];//将数据拷贝过去 */ } ~Test() { cout << "~Test" << endl; delete[] sum; } }; int main() { Test t1; t1.sum[0] = 1; t1.sum[1] = 2; t1.sum[2] = 3; t1.sum[3] = 4; Test t2 = t1; cout << t1.sum[2] << " " << t2.sum[2] << endl; // 3 3 t1.sum[2] = 100; cout << t1.sum[2] << " " << t2.sum[2] << endl;// 100 3 return 0; }
注意:
1、拷贝构造函数当中:t 引用的是 = 右值 const:只读 防止t类内变量被修改(深拷贝)
拷贝构造函数为什么要这样写?
//-------引用作为函数的形参--------- //直接作为函数的形参 #if 0 void func2(Test t)//Test t = t1;触发拷贝构造函数的调用,而且func函数的调用并且函数结束t需要销毁 { } #endif #if 1 void func2(const Test &t) { } //等价与指针void func2(Test *t) // 形参是个引用不会调用构造函数, // 也不会引起中间对象(t)的产生和销毁, // 使用const使其成为一个常量指针指向地址无法被修改 #endif
总代码工程:
/* * 《对象的析构》 * 定义:在C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数 * 析构函数没有参数也没有任何返回对象‘ * 析构函数在对象自动销毁的时候自动被调用 * 析构函数调用机制<<C++编译器自动调用 * */ #include <iostream> using namespace std; #if 0 class A { public: // char name[20]; // int age; int a1; int a2; int a3; //假如类中成员变量为指针 int *p; A()//无参数构造函数 { // p=(int *)malloc(40);//在堆上生成一块空间(C中常用在堆上创建空间的方法) cout << "A()." << endl; } A(int a,int b,int c):a1(10),a2(20),a3(30) { cout << "A(" << a1 << a2 << a3 << " ):a1(10),a2(20),a3(30)." << endl; cout << "a="<< a << endl; cout << "b="<< b << endl; cout << "c="<< c << endl; } //析构函数 ~A() { //在A对象销毁的时候,我们需要手动释放堆上的空间,放在析构函数当中 // free(p);//(C中常用释放堆空间的方法) cout << "~A()" <<endl; } }; //C++中对象的动态申请和释放 /* * new和delete的基本用法 * * new 运算符动态分配堆内存 * 使用形式:指针变量 = new类型(常量); * eg: int *p =new int(10); *explain: * 指针变量 = new类型[表达式]; * 作用:从堆上分配一块《类型》大小的存储空间 * 其中:“常量”是初始化值,可缺省 * 创建数组对象时,不能为对象指定初始值 * * delete 运算符释放已分配的内存空间 * 使用形式:delete 指针变量; * delete[]指针变量; * 其中指针变量必须是一个new返回的指针 *\ * new与delete和malloc与free的区别: * 注意:new和delete是运算符,而不是函数,因此执行效率高。 * 虽然为了与C语言兼容,C++仍保留malloc和free函数, * 但建议用户不用malloc和free函数, * 而用new和delete运算符。 * new/delete 和 malloc/free有何取别呢? * 1、malloc/free为C的标准库函数,new、delete则为C++的操作运算符 * 2、new能自动计算需要分配的内存空间,而malloc需要手工计算字节数 * 3、new与delete直接带具体类型的指针,malloc和free返回void类型的指针。 * 4、new类型是安全的,而malloc不是。例如int *p = new float[2];就会报错; * 而int *p = malloc(2 *sizeof(int))编译时编译器就无法指出错误来。 * 5、new调用构造函数,malloc不能;delete调用析构函数,而free不能 * 6、new/delete是操作符可以重载,malloc/free则不能 * 总结:更安全,便利,能重载 * */ //对象动态建立和释放 void Build_release(void) { //在堆上申请一个int类型大小的空间(4Bytes),并将申请的堆空间内容初始化为10 int *p = new int(10); delete p; //在堆上申请4个int类型大小的空间(4*sizeof(int)==16Bytes) 4:数组的长度 int *p2 = new int[4]; for (int i = 0; i < 4; i++) { cout << *(p2 + i) << endl; //cout << p2[i] << endl; } delete[] p2; //在堆上申请一个Box类型大小的空间 A *p3 = new A; //调用无参的构造函数 delete p3; //在堆上申请四个Box类型大小的空间 A *p4 = new A[4]; //调用无参的构造函数 delete[] p4; //在堆上申请一个Box类型大小的空间 A *p5 = new A(10, 10, 10); //调用带参数的构造函数 delete p5; } int main() { Build_release(); return 0; } #endif /* * 使用已构造的对象初始化新的对象 * 1、可以使用初始化好的对象,初始化另一个对象 * */ #if 1 class Test { public: int *sum; int x; int y; Test() { cout << "Test()" << endl; x=0; y=0; sum = new int[4]; } Test(int a,int b):x(a),y(b) { cout << "Test(int a,int b):x(a),y(b)" << endl; sum = new int[4]; } //拷贝构造函数 /* * 1、拷贝构造函数的原型 * 类名(const 类名 &变量名) * 2、拷贝构造函数的调用时机:使用一个构造好的对象初始化一个新的对象 * 深拷贝和浅拷贝的应用:当类的变量中含有指针类型的时候必须使用深拷贝, * */ Test(const Test &t)//t 引用的是 右值 const:只读 防止t类内变量被修改(深拷贝) { cout << "Test(const Test &t)" << endl; x = t.x; y = t.y; sum = new int(4);//重新给sum分配空间 for(int i=0;i<4;i++) sum[i] = t.sum[i]; } //析构函数释放申请的空间 ~Test() { cout << "~Test()" << endl; delete[] sum; } }; //无构造函数 void func(void) { Test *t1 = new Test(10,20);//调用构造函数:Test(int a,int b):x(a),y(b) t1->sum[0]=100; t1->sum[1]=101; t1->sum[2]=102; t1->sum[3]=103; Test t2 = *t1;//没有实现拷贝函数,编译器自动生成拷贝函数调用 cout << t2.sum[0] <<endl; cout << t2.sum[1] <<endl; cout << t2.sum[2] <<endl; cout << t2.sum[3] <<endl; cout << "t2.x="<<t2.x<<endl; cout << "t2.y="<<t2.y<<endl; cout << "t1.sum="<<t1->sum<<endl;//两个对象的指针指向同一个内存空间 cout << "t2.sum="<<t2.sum<<endl; //手动释放t1所指的申请的堆上的空间,意味着销毁 *t1这个对象,会自动调用析构函数 //顺带手动释放成员变量sum所申请的空间 delete t1; cout << t2.sum[0] <<endl; cout << t2.sum[1] <<endl; cout << t2.sum[2] <<endl; cout << t2.sum[3] <<endl; cout << "t2.sum="<<t2.sum<<endl; /* * 如果在类里边用一个对象给另一个对象赋值的时候如果一个对象为指针的话,那就回出问题 * */ //同一块堆空间将被释放两次会导致程序卡死 //how to solve... //挨个赋值、 // == / memcpy(x,y,size);将y的值付给x空间大小为size //拷贝构造函数(自己实现) // } //有拷贝构造函数 void func1(void) { Test t1(10,20);//调用构造函数:Test(int a,int b):x(a),y(b) t1.sum[0]=100; t1.sum[1]=101; t1.sum[2]=102; t1.sum[3]=103; //使用已经构造好对象的t1 初始化一个对象 Test t2 = t1;//调用拷贝构造函数 t2.Test(t1) cout << "t2.sum="<<t2.sum<<endl; cout << "t1.sum="<<t1.sum<<endl;//在拷贝构造函数当中重新给sum分配空间使 for(int i = 0;i<4;i++) cout << t2.sum[i] << endl; } //-------引用作为函数的形参--------- //直接作为函数的形参 #if 0 void func2(Test t)//Test t = t1;触发拷贝构造函数的调用,而且func函数的调用并且函数结束t需要销毁 { } #endif #if 1 void func2(const Test &t) { } //等价与指针void func2(Test *t) // 形参是个引用不会调用构造函数, // 也不会引起中间对象(t)的产生和销毁, // 使用const使其成为一个常量指针指向地址无法被修改 #endif int main(){ Test t1(10,20);//调用构造函数:Test(int a,int b):x(a),y(b) t1.sum[0]=100; t1.sum[1]=101; t1.sum[2]=102; t1.sum[3]=103; /* //使用已经构造好的对象t1,初始化一个新的对象 Test t2 = t1; cout << "t2.x="<<t2.x<<endl; cout << "t2.y="<<t2.y<<endl; cout << "t2.sum="<<t2.sum<<endl; cout << "t1.sum="<<t1.sum<<endl;//两个对象的指针指向同一个内存空间 //注意: */ // func(); // cout << "func():No illness" << endl; // func1(); // cout << "func1():Copied function" << endl; func2(t1);//现象调用拷贝构造函数 return 0; } #endif #if 0 #include <iostream> using namespace std; class Test { public: int *sum; int x; int y; Test() { cout << "Test()" << endl; x = 6; y = 0; sum = new int[4]; } Test(int a, int b):x(a), y(b) { cout << "Test(int a, int b)" << endl; sum = new int[4]; } }; int main() { Test t1(10, 20); //Test t1 = (10, 20); t1.sum[0] = 1; t1.sum[1] = 2; t1.sum[2] = 3; t1.sum[3] = 4; Test *t2 = &t1; cout << t1.x << " " << t2->x << endl; cout << t1.y << " " << t2->y << endl; cout << t1.sum[2] << " " << t2->sum[2] << endl; cout << t1.sum << " " << t2->sum << endl; t2->x=111; cout << t1.x << " " << t2->x << endl; return 0; } #endif