程序员面试100题之十五 和 三十, 含有指针成员的类的拷贝(异常安全的赋值运算符重载)

来源:互联网 发布:广电网络移机怎么办 编辑:程序博客网 时间:2024/05/22 04:54
// 程序员面试100题之十五 含有指针成员的类的拷贝.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <iostream>using namespace std;template<typename T> class Array_Wrong //含有指针的类复制时,会调用合成的默认复制构造函数,只复制指针的地址,这样容易出现悬垂指针。{public:Array_Wrong(unsigned arraySize):data(0), size(arraySize){if(size > 0)data = new T[size];}~Array_Wrong(){if(data) delete[] data;}void setValue(unsigned index, const T& value){if(index < size)data[index] = value;}T getValue(unsigned index) const{if(index < size)return data[index];elsereturn T();}private:T* data;unsigned size;};//按照C++primer上,有两种常有方式解决此问题,一是定义型值类(like string) 书上425页,二是智能指针 书上421页// 方法一: 智能指针 思想就是 用计数的方法来管理指针,所有的对象公用一个指针,有计数器类来管理//template<typename int> class Array_Intel_Ptr//定义计数类来管理指针,use 表示使用计数,所有成员均为private,让Array_Intel为其友元{friend class Array_Intel;// dont't forget the typeint *data;size_t use;Array_Intel_Ptr(int *p):data(p),use(1){}~Array_Intel_Ptr(){ delete data;}// noticeable };//template<typename int> class Array_Intel {public:Array_Intel(){}Array_Intel(unsigned arraySize):ptr(new Array_Intel_Ptr(0)), size(arraySize){cout<<" in constructor "<<endl;if(size > 0)ptr->data = new int[size];}Array_Intel(const Array_Intel& orig)//定义复制构造函数{cout<<" in copy constructor "<<endl;ptr=orig.ptr;size=orig.size;orig.ptr->use++;//same as ptr->use++}Array_Intel& operator=(const Array_Intel& orig)//重载赋值操作符const{//if (*this==orig)//没有两个对象的比较操作符,所以不能比较//{//return *this;//}cout<<" in = operater "<<endl;orig.ptr->use++;// still can use const why??????????????????????????????????????????????if (--ptr->use==0)// ++ and -- should be done first{cout<<" delete the ptr_date of first "<<ptr->data[0]<<endl;delete[] ptr->data;}ptr= orig.ptr;// this must be done at lastsize = orig.size;return *this;}~Array_Intel(){cout<<" in destructor "<<endl;if(ptr->use==0) delete[] ptr->data;}void setValue(unsigned index, const int& value){if(index < size)ptr->data[index] = value;}int getValue(unsigned index) const{if(index < size)return ptr->data[index];elsereturn int();}private:Array_Intel_Ptr* ptr;unsigned size;};template<typename T> class Array_Value// 型值类 思想既是 每次复制不是复制的指针,而是复制的指针内容,各个对象相互独立{public:// Array_Value(unsigned arraySize):data(new T(0)), size(arraySize) is wrongArray_Value(unsigned arraySize):data(NULL),size(arraySize)// data have to be initialized as NULL{cout<<" in constructor "<<endl;if(size > 0)data = new T[size];}Array_Value(const Array_Value &orig):data(NULL),size(orig.size){if (size>0)//necessarily{data = new T[size];memcpy(data,orig.data,sizeof(T)*size);}cout<<" in copy constructor "<<endl;}/*Array_Value& operator=(const Array_Value &orig)// thought1:here did not allocate memory, only in the constructor and copy constructor allocate memory// thought1 is wrong,{data = orig.data;// new T(orig.data) is wrongsize = orig.size;// delete orig.data; is wrongcout<<" in operator "<<endl;return *this;}*/Array_Value& operator=(const Array_Value &orig)// thought1 is wrong, this is different from that of C++ primer, for there real data is an int type, but here the real data is a Array{// this method turn out to be true for this time the program can be terminated normally2if(this == &orig)return *this;if (data!=NULL){delete[] data;data = NULL;//easily forgotten}size = orig.size;//T *tempT=NULL;T *temp=NULL;if (size>0)//necessarily{//?Array_Value temp(orig);//?tempT = temp.data;////  attention     attention         attention        attention        attention          attention         attention//?temp.data = data;//?data = tempT;//data = temp.data;// 只用赋值结果不好使,但是用交换结果却好使。为什么呢?因为调用析构函数的时候删除的是temp.data的指针,当用交换时,temp.data和data不是指向相同的地址,// 所以删除后,data不受影响。而只用赋值时,他们指向相同的地址空间,temp.data删除后,data的数据也没了。由此可知,复制构造函数申请空间的指针地址和析构函数删除空间的指针地址可以不相同。只是名字相同罢了。用变量也是一样,但是不能删除变量。temp = new T[size];if(temp!=NULL)memcpy(temp,orig.data,sizeof(T)*size);data = temp;// delete[] temp; 不应该删除temp,因为这里的值要传给data,temp = NULL;//原来的data = new T[size];//memcpy(data,orig.data,sizeof(T)*size); }cout<<" in operator "<<endl;return *this;}~Array_Value(){if(data!=NULL) {delete[] data;data = NULL;}cout<<" in destructor "<<endl;}void setValue(unsigned index, const T& value){if(index < size)data[index] = value;}T getValue(unsigned index) const{if(index < size)return data[index];elsereturn T();}private:T* data;unsigned size;};template<typename T> class Array // realize without Count Class, add anther private member count and another function to deal with the point{//only in assignment operator public:Array(unsigned arraySize):data(0), size(arraySize), count(new unsigned int){*count = 1;if(size > 0)data = new T[size];}Array(const Array& copy): size(copy.size), data(copy.data), count(copy.count){++ (*count);}~Array(){Release();}const Array& operator = (const Array& copy){if(data == copy.data)return *this;Release();// this must be done firstdata = copy.data;size = copy.size;count = copy.count;++(*count);// how can you guarantee the value of count of copy be changed also ? because count is a point to int and both in copy object or in current object the address of count is the same, so both values of count of two object are changed}void setValue(unsigned index, const T& value){if(index < size)data[index] = value;}T getValue(unsigned index) const{if(index < size)return data[index];elsereturn T();}private:T* data;unsigned size;unsigned int *count;// because count is an point to int, so in the copy constructor or in the operator the value of count in both of the current object or the transmitted original parameter can be changedvoid Release(){--(*count);if(*count == 0){if(data){delete []data;data = NULL;}delete count;count = 0;}} };int _tmain(int argc, _TCHAR* argv[]){/*Array_Intel arr(3);for (int i=0;i<3;i++){arr.setValue(i,2*i);}cout<<" arr is "<<endl;for (int i=0;i<3;i++){cout<<arr.getValue(i)<<" ";}cout<<endl;Array_Intel arr2(arr);Array_Intel arr3(2);for (int i=0;i<2;i++){arr3.setValue(i,i+100);}arr3=arr;// arr3 original data is deleted, then arr3 point to the area of arr, no extra memory is allocatedcout<<" arr2 is "<<endl;arr2.~Array_Intel();for (int i=0;i<3;i++){cout<<arr2.getValue(i)<<" ";}cout<<" arr3 is "<<endl;for (int i=0;i<2;i++){cout<<arr3.getValue(i)<<" ";}cout<<endl;*/Array_Value<int> arr4(3);for (int i=0;i<3;i++){arr4.setValue(i,2*i);}cout<<" arr4 is "<<endl;for (int i=0;i<3;i++){cout<<arr4.getValue(i)<<" ";}cout<<endl;system("pause");Array_Value<int> arr5(arr4);for (int i=0;i<3;i++){cout<<arr5.getValue(i)<<" ";}cout<<endl;Array_Value<int> arr6(2);// only allocate 2 memories, but what if it is copied by 3 memories, maybe this is the reason why the program can not be terminate normallyfor (int i=0;i<2;i++){arr6.setValue(i,i+100);}for (int i=0;i<2;i++){cout<<arr6.getValue(i)<<" ";}cout<<"before assign"<<endl;arr6=arr5;cout<<endl;system("pause");for (int i=0;i<3;i++){cout<<arr6.getValue(i)<<" ";}/*Array_Value<int> arr7(0);Array_Value<int> arr8(arr7);Array_Value<int> arr9(7);arr9 = arr8;Array<int> arr10(2);Array<int> arr11(3);// only allocate 2 memories, but what if it is copied by 3 memories, maybe this is the reason why the program can not be terminate normallyfor (int i=0;i<2;i++){arr10.setValue(i,i+100);}arr11=arr10;cout<<endl;for (int i=0;i<3;i++){cout<<arr11.getValue(i)<<" ";}Array<int> arr12(0);Array<int> arr13(arr12);//Array<int> arr14(7);arr11 = arr13;*/system("pause");return 0;}