对象的赋值与复制及拷贝函数:
-
对象的赋值:
例如:
int a = 5;int b = a;
这样的都称之为赋值操作,而对象的的赋值与之类似,也是用=
等号,不过赋值的目标不是int类型的整型数了,而是用一个对象给同类的另外一个对象赋值。此过程是通过成员复制来完成的,换句话说,就是将一个对象的成员的值一个一个的复制给另外一个对象的对应成员。举个栗子:
class CMyRect { public: CMyRect(int w = 0, int h = 0):width(w), height(h){}; public: int width; //矩形的宽 int height; //矩形的高 }; int main() { CMyRect rect_a(200, 100), rect_b; rect_b = rect_a; return 0; }
注意事项:
①. 对象的赋值,只是对对象的成员变量的赋值,对于成员函数来说不理会,也不做赋值处理,因为本身每个类的成员函数就一份而已所以也不需要赋值。
②. 类似上面的矩形类 CMyRect 的对象是可以直接用=等号赋值的,但是有一些成员是没办法用=等号赋值的,例如,成员变量是其他一种类对象,那么此时的解决办法就是重载类的=等号运算符。
③. 还有一种情况下,使用对象的赋值也是非常危险的,示例如下://CStudent类的实现: class CStudent{ public: char* p_name; char sex; int no; int age; CStudent(char* t_name, char t_sex, int t_no, int t_age) :sex(t_sex), no(t_no), age(t_age) //3.调用构造函数,初始化对象stud1 { int n_len = strlen(t_name); p_name = new char[n_len+1]; memset(p_name, 0, n_len+1); strcpy(p_name, t_name); cout << p_name<<"\t初始化完成"<<endl; } CStudent() //5.调用该构造函数,初始化对象stud2【注意】:此处未做任何操作,这就导致初始化后,p_name仍是个野指针,但这并非报错原因 { cout << p_name<<"\t初始完成"<<endl; } ~CStudent() //8.二者均调用析构函数,但是此处并不是并行,所以根据压栈规则可发现:stud2先调用,完成了对stud2.p_name指向空间的释放 //9. stud1后调用,完成了对stud1.p_name指向空间的释放【代码的意思这样,但是内存并不是呀,记得在第6.部时说明了二者指向地址相同,那么相同的地址空间被两次delete释放,当然就出现了内存报错】 { cout <<p_name<< "\t~CStudent()"<<endl; if(p_name) { delete [] p_name; } } }; //类测试函数: void test_xiGou() { CStudent stud1("zhangsan", 'f', 1001, 21); //2.实例化类对象stud1,在此处会调用带参构造函数CStudent(char* t_name, char t_sex, int t_no, int t_age), 初始化对象stud1 CStudent stud2; //4.实例化对象stud2,在此处调用无参构造函数CStudent(),初始化对象stud2 stud2 = stud1; //6.将stud1赋值给stud2,也就是说,现在stud1.p_name == stud2.p_name(二者指向了相同的地址) //7.本函数的声明周期即将完结,stud1和stud2也将消亡,此时二者均调用析构函数 } //主函数: int main() { test_xiGou(); //1.调用test_xiGou函数 return 0; }
注意我们在这里并非正常退出,报错码-1073740940 (0xC0000374)如上,查询得此为内存异常报错。
下面我们来探索一下原因:【见上对应代码注释】
-
对象的复制:
对象的赋值,是利用
=
将一个对象的成员变量赋值给另外一个同类对象的成员变量。可以说是对已经存在的两个对象进行操作。对象的复制,是一个对象从无到有的一个过程,在对象创建的时候就以一个已经存在的对象为源头进行创建本对象。
举个栗子:
Student zhangsan = {"zhangsan", 1002, 20}; Student lisi(zhangsan); Student wangwu = lisi;
-
复制/拷贝构造函数:
-
其实在2. 对象的复制中就用到了拷贝构造函数
Student lisi(zhangsan);
,根据他的名字,不难发现,他也是构造函数,不过他有些特殊而已。该函数的原型如下:class Student { public: char name[50]; int num; int age; Student(char* pname, int t_num, int t_age) :num(t_num), age(t_age) { strcpy(name, pname); } Student(Student& stud) //函数原型 { strcpy(name, stud.name); num = stud.num; age = stud.age; } };
-
函数特点:
①. 也是构造函数,所以函数名字就是类名字,并且无返回值;
②. 参数上有点特殊,参数是一般是本类的对象的引用;
③. 如果类没有提供拷贝构造函数,那么编译器会默认生成一个拷贝构造函数,作用比较单一,只是简单的将成员变量的值进行复制过去而已。 -
拷贝构造函数实现的必要性:
①. 类的成员变量中有一些无法进行赋值的,此时就需要自定义实现拷贝构造函数;
②. 针对类似上节课给大家讲解的 CStudent 类的实现方式上,调用起来也非常危险(情况和复制时问题差不多)
-