15.含有指针成员的类的拷贝(copy constructor)

http://zhedahht.blog.163.com/blog/static/25411174200722710364233/

http://www.cnblogs.com/t427/archive/2012/08/10/2633133.html

http://blog.csdn.net/gamecreating/article/details/5382902

http://www.cppblog.com/xczhang/archive/2008/01/21/41569.html

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


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.将拷贝构造函数和赋值构造函数改成私有,这样程序调用时编译器会报错。

分析:我们注意在类的内部封装了用来存储数组数据的指针。软件存在的大部分问题通常都可以归结指针的不正确处理。

这个类只提供了一个构造函数,而没有定义构造拷贝函数和重载拷贝运算符函数。当这个类的用户按照下面的方式声明并实例化该类的一个实例

Array A(10);

Array B(A);

或者按照下面的方式把该类的一个实例赋值给另外一个实例

Array A(10);

Array B(10);

B=A;

编译器将调用其自动生成的构造拷贝函数或者拷贝运算符的重载函数。在编译器生成的缺省的构造拷贝函数和拷贝运算符的重载函数,对指针实行的是按位拷贝,仅仅只是拷贝指针的地址,而不会拷贝指针的内容。因此在执行完前面的代码之后,A.data和B.data指向的同一地址。当A或者B中任意一个结束其生命周期调用析构函数时,会删除data。由于他们的data指向的是同一个地方,两个实例的data都被删除了。但另外一个实例并不知道它的data已经被删除了,当企图再次用它的data的时候,程序就会不可避免地崩溃。

由于问题出现的根源是调用了编译器生成的缺省构造拷贝函数和拷贝运算符的重载函数。一个最简单的办法就是禁止使用这两个函数。于是我们可以把这两个函数声明为私有函数,如果类的用户企图调用这两个函数,将不能通过编译。实现的代码如下:

private:
Array(const Array& copy);
const Array& operator = (const Array& copy);

2.添加二函数。

最初的代码存在问题是因为不同实例的data指向的同一地址,删除一个实例的data会把另外一个实例的data也同时删除。因此我们还可以让构造拷贝函数或者拷贝运算符的重载函数拷贝的不只是地址,而是数据。由于我们重新存储了一份数据,这样一个实例删除的时候,对另外一个实例没有影响。这种思路我们称之为深度拷贝。实现的代码如下:


public:
Array(const Array& copy):data(0), size(copy.size)
{
if(size > 0)
{
data = new T[size];
for(int i = 0; i < size; ++ i)
setValue(i, copy.getValue(i));
}
}

const Array& operator = (const Array& copy)
{
if(this == &copy)
return *this;

if(data != NULL)
{
delete []data;
data = NULL;
}

size = copy.size;
if(size > 0)
{
data = new T[size];
for(int i = 0; i < size; ++ i)
setValue(i, copy.getValue(i));
}
}

3.用引用计数器(指针)。即在类中新增一变量用来记录类中指针被拷贝的次数。当调用默认构造函数时将次数置1,在调用以上二函数时将计数器+1,在析构函数中将计数器-1,当计数器值=0时,表示没有对象使用它,这时才真的删除指针指向的内存(此行为可能发生在赋值构造函数及析构函数中)。

为了防止有多个指针指向的数据被多次删除,我们还可以保存究竟有多少个指针指向该数据。只有当没有任何指针指向该数据的时候才可以被删除。这种思路通常被称之为引用计数技术。在构造函数中,引用计数初始化为1;每当把这个实例赋值给其他实例或者以参数传给其他实例的构造拷贝函数的时候,引用计数加1,因为这意味着又多了一个实例指向它的data;每次需要调用析构函数或者需要把data赋值为其他数据的时候,引用计数要减1,因为这意味着指向它的data的指针少了一个。当引用计数减少到0的时候,data已经没有任何实例指向它了,这个时候就可以安全地删除。实现的代码如下:


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();

data = copy.data;
size = copy.size;
count = copy.count;
++(*count);
}

private:
void Release()
{
--(*count);
if(*count == 0)
{
if(data)
{
delete []data;
data = NULL;
}

delete count;
count = 0;
}
}

unsigned int *count;
}

15.含有指针成员的类的拷贝(copy constructor)

时间: 2024-10-09 07:43:03

15.含有指针成员的类的拷贝(copy constructor)的相关文章

含有指针变量的类需要重写拷贝构造函数,拷贝赋值函数,析构函数

编译器自带拷贝构造(ctor)和拷贝赋值函数(operator =), 但是对于成员变量含有指针的类,其不能使用默认的拷贝赋值函数.因为使用默认的,会直接将指针指向的地址进行赋值 (浅拷贝,共享内存,共指一个对象),而不是分配一块内存,具有相同的数值 (深拷贝,独立,两个对象). 浅拷贝容易造成dangling pointer. 用一个例子来展示: 1 #ifndef __MYSTRING__ 2 #define __MYSTRING__ 3 4 class String{ 5 public:

C++ 带有指针成员的类处理方式

在一个类中,如果类没有指针成员,一切方便,因为默认合成的析构函数会自动处理所有的内存.但是如果一个类带了指针成员,那么需要我们自己来写一个析构函数来管理内存.在<<c++ primer>> 中写到,如果一个类需要我们自己写析构函数,那么这个类,也会需要我们自己写拷贝构造函数和拷贝赋值函数. 析构函数: 我们这里定义一个类HasPtr,这个类中包含一个int 类型的指针.然后定义一个析构函数,这个函数打印一句话. HasPtr.h 类的头文件 1 #pragma once 2 #if

C++11 指针成员与拷贝构造(浅拷贝与深拷贝)

[1]浅拷贝 一直以来,设计一个类,个人认为,最能体现水平的地方在于:类中含有指针成员变量. 如下一个典型的浅拷贝示例: 1 #include <iostream> 2 using namespace std; 3 4 class HasPtrMem 5 { 6 public: 7 HasPtrMem() : d(new int(0)) 8 {} 9 ~HasPtrMem() 10 { 11 delete d; 12 d = nullptr; 13 } 14 15 int* d; 16 };

C++ Primer 学习笔记_57_类与数据抽象 --管理指针成员

复制控制 --管理指针成员 引言: 包含指针的类需要特别注意复制控制,原因是复制指针时只是复制了指针中的地址,而不会复制指针指向的对象! 将一个指针复制到另一个指针时,两个指针指向同一对象.当两个指针指向同一对象时,可能使用任一指针改变基础对象.类似地,很可能一个指针删除了一对象时,另一指针的用户还认为基础对象仍然存在.指针成员默认具有与指针对象同样的行为. 大多数C++类采用以下三种方法之一管理指针成员: 1)指针成员采取常规指针型行为:这样的类具有指针的所有缺陷但无需特殊的复制控制! 2)类

Copy constructor拷贝构造函数

翻译的是wikipedia关于copy constructor,地址:点击打开链接 拷贝构造函数是C++语言中用一个已有对象创建一个新对象的特殊构造函数,该函数的第一个参数必须是函数所在类型的引用(译注:const/non-const都可以,可以有多个参数剩余参数必须有默认值,一定要是引用,这些原因见后,问:有多个参数拷贝构造如何调用?). 通常编译器会自动为每一个class创建一个拷贝构造函数(显示拷贝构造);有些情况下程序员自定义了拷贝构造函数(用户自定义拷贝构造),这时编译器不合成拷贝构造

C++ Copy Constructor in depth (深入理解C++拷贝构造函数)

The copy constructor is a special kind of constructor which creates a new object which is a copy of an existing one, and does it efficiently. (拷贝构造函数是一种特别的构造函数,用于复制已经存在的对象到新生成的对象,这是一种高效的方式.) Here below is a simple declaration of a copy constructor: (

【C/C++学院】0819-/类的成员函数与const-mutable /构造与析构/拷贝构造deletedefault以及深浅拷贝/静态成员函数成员变量类在内存的存储默认参数/友元类以及友元函数

类的成员函数与const-mutable 成员函数 Fushu.h #pragma once #include <iostream> class fushu { public: int x; int y; public: fushu(); ~fushu(); void show(); inline void showall(int x, int y);//显式内联 void setxy(int x, int y);//编译器优化,默认隐式内联 void show(int x, int y);

类拓展——拷贝控制成员

一.拷贝控制操作之于类 作用:定义类对象拷贝.移动.赋值或销毁时做什么 没有定义:编译器会为我们定义,但合成版本的行为可能并非我们所想 二.拷贝构造函数 1. 每个成员的类型决定了它如何拷贝 类类型的成员,会使用其拷贝构造函数来拷贝:内置类型的成员则直接拷贝. 对于数组,合成拷贝构造函数会逐元素地拷贝一个数组类型的成员. 2. 细节 第一个参数是自身类类型的引用,且任何额外参数都有默认值 通常不应该是explicit的,因为它经常会被隐式地使用 即使定义了拷贝构造函数,编译器也会生成合成版本 三

C++的继承操作---基类指针访问派生类问题---基类成员恢复访问属性问题

#include "stdafx.h" #include <iostream> #include <algorithm> using namespace std; class Base { public: int num; virtual void func() { cout<<"Do something in Base"<<endl; } }; class Derived:private Base { public: