`

程序员面试题精选(15)-含有指针成员的类的拷贝

阅读更多

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

C++代码 复制代码
  1. template<typename T> class Array   
  2. {   
  3. public:   
  4.        Array(unsigned arraySize):data(0), size(arraySize)   
  5.        {   
  6.             if(size > 0)   
  7.                    data = new T[size];   
  8.        }   
  9.   
  10.        ~Array()   
  11.        {   
  12.             if(data) delete[] data;   
  13.        }   
  14.   
  15.       void setValue(unsigned index, const T& value)   
  16.        {   
  17.             if(index < size)   
  18.                    data[index] = value;   
  19.        }   
  20.   
  21.        T getValue(unsigned index) const  
  22.        {   
  23.             if(index < size)   
  24.                   return data[index];   
  25.             else  
  26.                   return T();   
  27.        }   
  28.   
  29. private:   
  30.        T* data;   
  31.       unsigned size;   
  32. };  
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;
};


分析:我们注意在类的内部封装了用来存储数组数据的指针。软件存在的大部分问题通常都可以归结指针的不正确处理。
这个类只提供了一个构造函数,而没有定义构造拷贝函数和重载拷贝运算符函数。当这个类的用户按照下面的方式声明并实例化该类的一个实例
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);
最初的代码存在问题是因为不同实例的data指向的同一地址,删除一个实例的data会把另外一个实例的data也同时删除。因此我们还可以让构造拷贝函数或者拷贝运算符的重载函数拷贝的不只是地址,而是数据。由于我们重新存储了一份数据,这样一个实例删除的时候,对另外一个实例没有影响。这种思路我们称之为深度拷贝。实现的代码如下:

C++代码 复制代码
  1. public:   
  2.        Array(const Array& copy):data(0), size(copy.size)   
  3.        {   
  4.             if(size > 0)   
  5.              {   
  6.                    data = new T[size];   
  7.                   for(int i = 0; i < size; ++ i)   
  8.                          setValue(i, copy.getValue(i));   
  9.              }   
  10.        }   
  11.   
  12.       const Array& operator = (const Array& copy)   
  13.        {   
  14.             if(this == ©)   
  15.                   return *this;   
  16.   
  17.             if(data != NULL)   
  18.              {   
  19.                   delete []data;   
  20.                    data = NULL;   
  21.              }   
  22.   
  23.              size = copy.size;   
  24.             if(size > 0)   
  25.              {   
  26.                    data = new T[size];   
  27.                   for(int i = 0; i < size; ++ i)   
  28.                          setValue(i, copy.getValue(i));   
  29.              }   
  30.        }  
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 == ©)
                  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));
             }
       }


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

C++代码 复制代码
  1. public:   
  2.        Array(unsigned arraySize)   
  3.              :data(0), size(arraySize), count(new unsigned int)   
  4.        {   
  5.              *count = 1;   
  6.             if(size > 0)   
  7.                    data = new T[size];   
  8.        }   
  9.   
  10.        Array(const Array& copy)   
  11.              : size(copy.size), data(copy.data), count(copy.count)   
  12.        {   
  13.              ++ (*count);   
  14.        }   
  15.   
  16.        ~Array()   
  17.        {   
  18.              Release();   
  19.        }   
  20.   
  21.       const Array& operator = (const Array& copy)   
  22.        {   
  23.             if(data == copy.data)   
  24.                   return *this;   
  25.   
  26.              Release();   
  27.   
  28.              data = copy.data;   
  29.              size = copy.size;   
  30.              count = copy.count;   
  31.              ++(*count);   
  32.        }   
  33. private:   
  34.       void Release()   
  35.        {   
  36.              --(*count);   
  37.             if(*count == 0)   
  38.              {   
  39.                   if(data)   
  40.                    {   
  41.                         delete []data;   
  42.                          data = NULL;   
  43.                    }   
  44.   
  45.                   delete count;   
  46.                    count = 0;   
  47.              }   
  48.        }   
  49.   
  50.       unsigned int *count;   
分享到:
评论

相关推荐

    C/C++程序员面试指南.杨国祥(带详细书签).pdf

    面试题6:简述类成员函数的重写、重载和隐藏的区别 面试题7:简述多态实现的原理 第10章 数据结构 10.1 链表 面试题1:链表和数组有什么区别 面试题2:寻找单链表中间结点 面试题3:怎样把一个单链表反序 10.2 单...

    程序员面试100题数据结构和算法

    15. **含有指针成员的类的拷贝**:处理含有指针的类的深拷贝和浅拷贝问题,需要重载赋值运算符和复制构造函数,确保指针成员被正确复制和释放。 16. **Fibonacci数列的O(logn)求解**:使用矩阵快速幂可以在对数时间...

    C++程序员面试题集

    "C++程序员面试题集" 本文档总结了C++基础知识,涵盖了面试过程中可能出现的所有问题。下面是从文件中提炼出的相关知识点: 1. 面向对象的程序设计思想是什么? 答:把数据结构和对数据结构进行操作的方法封装形成...

    c或c++语言程序员面试精选

    "C或C++语言程序员面试精选"提供了一份宝贵的资源,旨在帮助开发者准备面试,并深化对这两种语言的理解。这份文档不仅适用于正在寻找工作的程序员,也适合任何希望巩固C/C++基础的学习者。 C语言,由Dennis Ritchie...

    程序员面试精选题

    友元关系允许一个类访问另一个类的私有和保护成员,但这种关系是非对称的、不可传递的,且不能被继承。这意味着友元关系只能在声明中指定的类之间生效,不会自动扩展到子类或其他类。 #### 25. 对象成员初始化的...

    笔试题-经典LINUX程序员面试题.docx

    【Linux程序员面试知识点详解】 1. **Linux发行版与软件版本**: Linux有众多发行版,如Ubuntu、CentOS、Fedora、Debian等。GCC(GNU Compiler Collection)是Linux下的编译器,用于将源代码编译为可执行程序。...

    笔试题-经典LINUX程序员面试题.pdf,这是一份不错的文件

    这份“笔试题-经典LINUX程序员面试题.pdf”涵盖了Linux程序员所需掌握的关键知识点,包括基础题、程序阅读题、程序写作题、英语基础题和选择题。以下是各部分的主要内容: **一、基础题** 1. **Linux发行版和版本...

    C/C++程序员面试宝典

    《C/C++程序员面试宝典》是一本专为准备C/C++编程面试的求职者精心编写的指南。这本书以PDF格式提供,具有清晰的目录结构,使得读者可以快速定位到感兴趣或需要复习的知识点,有助于高效学习和查阅。在追求理想的...

    CC++程序员应聘常见面试题

    【C/C++程序员应聘常见面试题】通常涵盖了基础语法、内存管理、字符串处理、指针操作、算法和数据结构等多个方面。以下是一些常见的知识点: 1. **字符串处理**: - **strcpy函数**:面试中常问的问题是要求应聘者...

    c++面试题汇总(包括程序员宝典,面试试题,c++面试宝典等)

    这份"c++面试题汇总"集合了程序员宝典、面试试题和C++面试宝典等多个资源,旨在帮助求职者全面准备C++相关的面试。以下将对一些关键知识点进行详细阐述: 1. **基础语法**: - **变量与数据类型**:理解基本数据...

    深圳市雁联计算系统有限公司C++程序员面试题

    ### 深圳市雁联计算系统有限公司C++程序员面试题解析 #### 1. 栈与堆的申请方式及系统响应方式的区别 在C++编程中,**栈**和**堆**是两种不同的内存区域,它们在申请方式和系统响应上存在显著差异。 - **栈**内存...

    C,C++程序员应聘常见面试试题剖析(超级详细解析).doc

    总结来说,C/C++面试题通常涵盖语言基础、内存管理、数据结构、算法等多个方面。通过分析这些题目,程序员不仅可以检验自己的技术功底,还能发现自身的不足,进而提高编程技能。无论是在面试还是日常开发中,对细节...

    C/C++程序员应聘常见面试题深入剖析

    《C/C++程序员应聘常见面试题深入剖析》 在C/C++编程领域,面试往往是对开发者基础技能和问题解决能力的直接检验。一道看似简单的题目,如要求编写一个`strcpy`函数,实则是对程序员对内存管理、字符串处理以及安全...

    程序员面试宝典-第三版(高清带目录)

    第1部分 求职过程  章 应聘求职  1.1 渠道  1.2 流程  第2章 简历书写  2.1 简历注意事项  2.2 简历模板  第3章 求职五步曲  3.1 笔试  3.2 电话面试 ...第5部分 综合面试题 附录A 面试经历总结

    C、C++程序员应聘常见面试题深入剖析

    《C、C++程序员应聘常见面试题深入剖析》 面试是评估程序员技能的重要环节,尤其在C和C++这样的编程语言领域,面试题往往聚焦于基础概念和技术深度。本文将探讨一些常见的C/C++面试题,并通过具体示例来解析其中...

    c-c++常见面试题总结

    在C和C++的世界里,面试题通常涵盖了广泛的主题,从基本语法到高级设计原则,再到内存管理和模板元编程。以下是一些常见的C++面试题及其解析,它们可以帮助你为面试做好充分准备。 1. **指针与引用** - 指针是C++...

Global site tag (gtag.js) - Google Analytics