含有指针成员的类的拷贝

题目:下面是一个数组类的声明与实现。请分析这个类有什么问题,并针对存在的问题提出几种解决方案。

template<typename T>

class Array
{
public:
    Array(unsignedarraySize):data(0), size(arraySize)
    {
       if(size> 0)
           data = newT[size];
    }

    ~Array()
    {
       if(data)delete[] data;
    }

    voidsetValue(unsigned index, const T& value)
    {
       if(index< size)
           data[index] = value;
    }

    T getValue(unsignedindex) const
    {
       if(index< size)
           returndata[index];
       else
           returnT();
    }

private:
    T* data;
    unsignedsize;
};


分析:这个类中有指针成员变量,在这种情况下,如果处理不当,很容易造成内存泄露问题。

该类没有提供拷贝构造函数和赋值(=)运算符的重载函数,当需要调用拷贝构造函数或者赋值操作时,编译器会自动调用系统生成的默认拷贝构造函数或者默认赋值运算符重载函数,而这两个默认的函数是以逐个拷贝成员变量的方式来赋值数据成员,在这种拷贝方式下,赋值指针被定义为将一个变量的地址赋给另一个变量,也即两个指针指向同一个地址,这种隐式的指针复制结果就是两个对象拥有指向同一个动态分配的内存空间的指针。当释放第一个对象的时候,它的析构函数就会释放与该对象有关的动态分配的内存空间。而释放第二个对象的时候,它的析构函数会释放相同的内存,这样是错误的(两次释放同一块内存是错误的做法,而且很可能造成堆的奔溃)。

所以,如果按如下方式调用就会出现内存泄露问题:

Array<int> A(20);

Array<int> B(A);

或者

Array<int> A(20);

Array<int> B(10);

A = B;

编译器会自动调用默认拷贝构造函数和默认赋值运算符重载函数,而这两个默认的函数对指针都是按位拷贝,只拷贝地址,而不会把指针所指向的内容做拷贝,因此当A.data和B.data指向同一位置,当A或者B中任意一个结束生命周期时,就会delete data,由于A和B指向的是同一位置,所以两个实例的data都被delete了,所以会出现两次释放同一片内存空间的问题,这样的做法是错误的。


解决方案1: 禁用拷贝构造函数和赋值运算符重载函数

private:

Array(const Array& array);

const Array& operator=(const Array& array);


解决方案2:添加拷贝构造函数和赋值运算符重载函数,并且合理的实现。

public:

Array(const Array& array);

const Array& operator=(const Array& array);


template<typename T>

Array<T>::Array(const Array&array): data(0), size(array.size)

{

if(size > 0)

{

data = new T[size];

for(int i = 0; i < size; i++)

setValue(i, array.getValue(i));

}

}


template<typename T>

const Array<T>& Array<T>::operator=(const Array<T>& array)

{

if(this == &array)

return *this;


if(data != NULL)

{

delete[] data;

data = NULL;

}


size = array.size;

if(size > 0)

{

data = new T[size];

for(int i = 0; i < size; i++)

setValue(i, array.getValue(i));

}

}


解决方案3:用引用计数来管理指针成员

public:

     Array(const Array& array);

const Array& operator=(const Array& array);


template<typename T>

Array<T>::Array(unsignedarraySize):data(0), size(arraySize)
 {

count = 1;

if(size> 0)

          data = newT[size];

 }

template<typename T>

Array<T>::Array(const Array&array): data(0), size(array.size), count(array.count)

{

++count;

}


template<typename T>

const Array<T>& Array<T>::operator=(const Array<T>& array)

{

if(data == array.data)

return *this;


release();

data = array.data;

size = array.szie;

count = array.count;

++count;

}


template<typename T>

Array<T>::~Array()

{

release();

}


private:

void release()

{

--count;

if(count == 0)

{

if(data)

{

delete[] data;

data = NULL;

}

}

}


因此,如果一个类里面有指针成员变量,要么必须显示的写拷贝构造函数和重载赋值运算符,要么禁用拷贝构造函数和重载赋值运算符。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值