15 -含有指针成员的类的拷贝
程序员面试精选类博客题目出自何海涛的网易博客,本博客只记录自己的实现,以供学习。
题目:给出了一个模板类Array的声明与实现,问该代码有什么问题,如何解决。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
template<typename T> class Array { public: Array(unsigned arraySize):data(0), size(arraySize) { if(size > 0) data = new T[size]; } ~Array() { 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]; else return T(); } private: T* data; unsigned size; };
分析:类中有指针变量,即深浅拷贝问题。因为类中没有实现拷贝构造函数和赋值构造函数,因此在用到拷贝构造函数和赋值构造函数的时候只会将指针简单复制,这样二个对象的这个指针会指向同一块内存,在对象删除及调用时会造成程序崩溃。
解决方法:原文给出三种解决方案。
1.将拷贝构造函数和赋值构造函数改成私有,这样程序调用时编译器会报错。
2.添加二函数
3.用引用计数器(指针)。即在类中新增一变量用来记录类中指针被拷贝的次数。当调用默认构造函数时将次数置1,在调用以上二函数时将计数器+1,在析构函数中将计数器-1,当计数器值=0时,表示没有对象使用它,这时才真的删除指针指向的内存(此行为可能发生在赋值构造函数及析构函数中)。
本文实现了方案2,如下:
个人实现:
Array(const Array& arry) { //规范写法:size和data可以放在初始化列表里 size = arry.size; if (arry.size>0) { data = new T[size]; for (int i=0;i<size;++i) { //可以利用setValue()及getValue() *(data+i)=arry.data[i]; } } else { size = 0; data = 0; } } Array& operator=(const Array& arry) { //注意,当时忘写 if (this == &arry) { return *this; } //注意,当时忘写 if(data != NULL) { delete [] data; data = 0; } this->size = arry.size; if(arry.size>0) { this->data = new T[size]; for (int i=0;i<size;++i) { *(this->data+i)=arry.data[i]; } } else { this->data = 0; } return *this; }
疑惑:
//原实现声明 const Array& operator = (const Array& copy) //自己的声明 Array& operator = (const Array& copy)
原以为const修饰函数返回值意为不可修改,但测试发现此const的返回值依然可以修改,不明原因……
解释:返回const引用的赋值函数只对链式表达式的实现有影响。
类定义如下,下面二个链式表达式都是可以编译通过的。
class A { A & operate = (const A & other); // 赋值函数 } ; A a, b, c; // a, b, c 为A 的对象 a = b = c; // 正常的链式赋值 (a = b) = c; // 不正常的链式赋值,但合法
若A的赋值构造函数形式如下:
const A & operate = (const A & other); // 赋值函数
则a = b = c;是正常的。(a = b) = c;是错误的。
原因在于(a = b) = c先执行(a = b),这个赋值函数返回的是Const A&,即a的const引用,之后执行=c相当于const a=c,当然就出错了。
参考链接:http://blog.csdn.net/gamecreating/article/details/5382902
心得:
在赋值构造函数中要先判断赋值的是否为自己,释放当前对象指针内存以免出现悬垂指针。
以及const函数及非const函数的调用规则:
T getValue(unsigned index) const { cout<<" get by const function;"; if(index < size) return data[index]; else return T(); } T getValue(unsigned index) { cout<<" get by no const function;"; if(index < size) return data[index]; else return T(); }
众所周知,在相同参数及相同名字的情况下,const是可以构成函数重载的,但const成员函数不能更改任何非静态成员变量;
在二函数都存在的情况下:
const对象默认调用const成员函数,非const对象默认调用非const成员函数;
若非const对象想调用const成员函数,则需显示转化,如(const Array<int>&)obj.getValue(0);
若const对象想调用非const成员函数,同理const_cast<Array<int>&>(constObj).getValue(0);(注意:constObj要加括号)
当只有一函数存在的情况下:
非const对象可以调用const成员函数或非const成员函数;
const对象只能调用const成员函数;
……