`
蒙面考拉
  • 浏览: 160273 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
社区版块
存档分类
最新评论

复制构造函数(拷贝构造函数)以及深浅拷贝

 
阅读更多

对于普通对象而言复制是很简单的,一般是将变量或者常量赋值给某一个变量:int a=88;int a=b;而对于类对象而言,由于其内部结构复杂,复制过程便有一些道道:

#include <iostream>
using namespace std;

class CExample {
private:
     
int a;
public:
     CExample(
int b)
     
{ a=b;}
     
void Show ()
     
{
        cout
<<a<<endl;
    }

}
;

int main()
{
     CExample A(
100);
     CExample B
=A;
     B.Show ();
     
return 0;
}   代码的输出为:100。系统为对象B分配了内存并且完成了与对象A的复制过程。就对象而言,相同类型的类对象是通过拷贝构造函数来完成整个复制的

 

把参数传递给函数有三种方法:值传递,传地址,传引用。前者与后两者不同的地方在于:当使用值传递的时候,会在函数里面生成传递参数的一个副本,这个副本的内容是按位从原始参数那里复制过来的,两者的内容是完全相同的。当原始参数是一个对象的时候,它也会产生对象的副本。我们需要注意的是:一般对象产生时会触发构造函数,但是产生对象的副本时执行的却是复制构造函数,也就是拷贝构造函数。为什么呢?一般的构造函数完成的是一些成员属性初始化工作,对象传递给某一个函数时,对象的某些属性可能已经被改变了,如果再产生对象副本时再执行对象的构造函数,可能会使对象的属性又再恢复到原始状态。所以在产生对象副本时会执行一个默认的复制构造函数。但是新的问题出现了:在函数执行完返回的时候,对象副本会执行析构函数,如果你的析构函数是空的话没有任何问题,但是一般的析构函数会执行一些清理工作如释放指针所指向的内存,这时问题就出现了。假如构造函数里为一个指针变量分配了内存,在析构函数里面释放分配给这个指针所指向的内存空间。在使用对象进行值传递时,首先有一个对象的副本产生了,这个副本也有一个指针,它和原始对象的指针指向同一个内存空间。在函数返回时,对象副本的析构函数将会执行,即释放对象副本里面指针所指向的内存空间,但是这个内存空间对原始对象还是有用的,对程序本省而言这是一个严重的错误。并且当原始对象被销毁的时候析构函数会再次执行,对同一块系统动态分配的内存空间再次释放,将会产生严重的错误。

 

我们解决上述问题的办法就是拷贝构造函数==复制构造函数

概念:特殊的构造函数,它的唯一的一个参数是对本对象的引用,是不可改变的。这个函数一般用在下面三种情况:

          1.一个对象以值传递的方式传入函数体

          2.一个对象以值传递的方式从函数返回

          3.一个对象被另一个同类对象初始化

补充:初始化与赋值的不同:

          初始化:创建一个新的对象,并且初值来源于一个已存在的对象

          赋值:在已存在的两个对象之间进行的,此时没有创建任何对象,其实就是给一个已存在的对象一个新值

          int a=3;// 初始化     Thing t=x;//初始化,调用拷贝构造函数

          a=4;//赋值               t=x;//赋值,调用的operator=操作符函数,初始化调用的是构造函数

如果在类中没有显式的声明一个拷贝构造函数,编译器会私下制定一个函数来进行对象之间的位拷贝,也就是浅拷贝。

 

开头代码的带有拷贝构造函数的版本:

#include <iostream>
using namespace std;

class CExample {
private:
    
int a;
public:
    CExample(
int b)
    
{ a=b;}
    
    CExample(
const CExample& C)
    
{
        a
=C.a;
    }

    
void Show ()
    
{
       cout
<<a<<endl;
    }

}
;

int main()
{
    CExample A(
100);
    CExample B
=A;
    B.Show ();
    
return 0;
}
 

 当用一个已初始化过的自定义类类型对象去初始化另一个新构造的对象时,拷贝构造函数会被自动调用

 

 深浅拷贝的由来及作用:

 

        某些情况下,类内成员变量需要动态的开辟内存,如果实行位拷贝,也就是浅拷贝,也就是默认生成的拷贝,会把对象里的值完全复制给另一个对象,如A=B。如果B中有一个成员变量指针已经申请了内存,那么A中的那个指针成员变量也会指向同一块内存。已经开辟的一端堆地址原来是属于对象A的,因为由于复制过程发生,B对象取得的是a已经开辟的堆地址。当B把内存释放,这时A的指针就变成了野指针,会出现严重的错误。如何解决这个问题呢?

 

下面将给出深拷贝的代码示例:

 

#include <iostream>
using namespace std;
class CA
{
 public:
  CA(int b,char* cstr)
  {
   a=b;
   str=new char[b];//初始化时开辟内存
   strcpy(str,cstr);
  }
  CA(const CA& C)
  {
   a=C.a;
   str=new char[a]; //深拷贝的体现
   if(str!=0)
    strcpy(str,C.str);
  }
  void Show()
  {
   cout<<str<<endl;
  }
  ~CA()
  {
   delete str;
  }
 private:
  int a;
  char *str; //指针变量
};

int main()
{
 CA A(10,"Hello!");
 CA B=A;
 B.Show();
 return 0;
}
上述代码演示了如何用深拷贝解决上述问题的方法:对象B的str属性采取了新开辟内存的方式避免了归属不清所导致析构函数释放空间的错误。对象副本在调用析构函数时释放的空间是自己在复制过程中新开辟的内存空间,与原始对象的内存空间没有什么联系。

深拷贝和浅拷贝的区别:

      如果一个类拥有资源(堆或者其他系统资源),当这个类的对象发生复制过程的时候,被赋值的对象会开辟新的资源,这个过程叫做深拷贝。反之对象存在资源但复制过程并未复制资源,即没有开辟新的资源的情况视为浅拷贝。

      浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错,这点尤其需要注意!
其他网络资源:http://blog.csdn.net/feiyond/article/details/1807068

                     http://pcedu.pconline.com.cn/empolder/gj/c/0503/570112.html

                     http://hi.baidu.com/pweiwen/blog/item/6d3edf7e329f693d0cd7da37.html

 

 

 

 

 

 

 

 

 

 

分享到:
评论

相关推荐

    16-17 秋冬《面向对象程序设计》 Final Examination with answer1

    拷贝构造函数用于创建新对象作为现有对象的副本,这里的拷贝构造函数确保了正确地复制了所有数据成员,包括子对象`A`,实现了深拷贝。 4. **异常处理**: `Exception`类用于处理异常情况,包含一个构造函数和一个...

    精品资料面向对象程序设计实验版&#40;附全部实验代码&#41;-精品资料.pdf

    总的来说,这个实验涵盖了面向对象编程的基础,包括类的设计、对象的创建和使用、构造函数的实现、以及深浅拷贝构造函数的区别。通过实践,学习者将更好地理解C++中面向对象编程的核心概念,并能够运用到实际项目中...

    c++中深浅拷贝以及写时拷贝的实现示例代码

    浅拷贝在拷贝构造函数或赋值运算符中仅仅复制对象的引用,而不是创建新的内存空间。这意味着两个对象共享同一块内存,当其中一个对象改变,另一个对象的状态也会受到影响。这可能导致意外的数据共享和错误的析构行为...

    c++类和对象

    - **浅拷贝**:在浅拷贝中,拷贝构造函数只是简单地复制原对象的指针,这意味着两个对象实际上指向内存中的同一块区域。当其中一个对象被销毁时,这块内存可能被释放两次,导致程序崩溃。在实验中,`Person p2 = p1;...

    symbian笔试题目啊c++/java

    拷贝构造函数用于初始化新对象为已存在对象的副本,不重载可能导致意外的浅复制。赋值运算符`=`重载处理对象间的赋值,防止深浅拷贝问题,不重载可能导致对象状态错误。相等运算符`==`重载用于自定义对象的比较逻辑...

    C C++ linux 面试题

    10. **拷贝构造与深浅拷贝**:拷贝构造函数用于复制对象,浅拷贝只复制指针,可能导致资源管理问题(如双重释放)。深拷贝复制实际资源,确保独立性。临时对象是在表达式中创建的临时实体,用于优化性能。 11. **...

    C++面试宝典2015版1

    6. **拷贝构造和深浅拷贝**: - 浅拷贝只是复制指针,如果对象内部含有指针,拷贝后两个对象会共享同一块内存区域,改变一个会影响另一个。 - 深拷贝会为每个对象分配新的内存,确保拷贝的对象独立。 7. **多态、...

    <<C++Primer课后习题解答>>word版

    第十三章 复制控制:讲解拷贝构造函数、赋值运算符以及深浅拷贝的概念。习题通常涉及编写正确的复制控制机制,以确保对象正确地被复制和赋值。 这些章节的习题解答涵盖了C++编程的基础到高级概念,通过解答可以深化...

    C++软件

    #### 十、拷贝构造函数与深浅拷贝 - **浅拷贝** 只复制对象的引用,而不是实际对象本身。如果对象包含指针,则浅拷贝只会复制指针值。 - **深拷贝** 不仅复制对象本身,还复制对象所指向的数据。这对于包含指针的...

    javascript深拷贝和浅拷贝详解

    阮一峰提出的`object()`函数利用了构造函数和原型链来实现深拷贝。 **三、浅拷贝** 浅拷贝通常使用`Object.assign()`、扩展运算符`...`或克隆方法(如`jQuery.extend(false, target, source)`)实现。这些方法只会...

    字符串-关于字符串的创建-常见函数

    数组的深浅拷贝涉及到值类型和引用类型的区别,深拷贝会复制整个结构,而浅拷贝只复制表面层次。 数组提供了一系列API,如`length`属性返回数组元素的个数,`pop()`和`push()`分别用于删除和添加数组末尾的元素,`...

    JavaScript实现浅拷贝与深拷贝的方法分析

    了解和掌握深浅拷贝的概念以及在JavaScript中如何实现它们,对于编写健壮的代码至关重要。这不仅可以避免意外地改变原始数据,还能提高代码的可维护性和性能。在实际开发中,根据具体场景选择合适的方法,可以有效地...

    C++面向对象1-11.zip

    理解类和对象的概念,掌握构造函数、析构函数以及成员函数的使用,是掌握C++的基础。 4. **类的继承**:继承是面向对象中的一个重要特性,允许我们创建新的类(子类)来扩展或修改已存在的类(父类)。通过继承,...

    JavaScript对象与数组参考大全

    创建对象主要有三种方式:字面量语法、构造函数以及Object.create()方法。 **字面量语法**: ```javascript let obj = { key1: value1, key2: value2, }; ``` **构造函数**: ```javascript let obj = new ...

    软件测试开发面试经验参考文档

    1. 深浅拷贝:浅拷贝是构造一个新的集合对象,用原始对象中找到的子对象的引用来填充它,复制过程不会递归,不会创建子对象的副本。深拷贝是构造一个新的集合对象,递归地用在原始对象中找到的子对象的副本来填充它...

    字符串的基本操作与基本内容

    1. **创建字符串**:在大多数编程语言中,创建字符串可以通过直接赋值或者使用构造函数来实现。例如,在Python中,`str = "Hello, World!"`,而在C++中,`string str = "Hello, World!";`。 2. **访问字符串**:...

    面试题++++++.md

    #### JavaScript中的深浅拷贝 - **区别:** - 浅拷贝仅复制对象的第一层属性。 - 深拷贝会递归复制所有层级的属性。 - **实现方法:** - **递归:** 对于复杂对象使用递归方式复制。 - **$.extend:** jQuery ...

    python面试常用的常用38题

    Python中的深浅拷贝可以用来复制对象。浅拷贝只复制对象的引用,而深拷贝可以复制对象的所有元素。 8. Python中的文件读取 Python中的文件读取可以使用open函数或with语句来实现。例如,with open('file.txt', 'r'...

Global site tag (gtag.js) - Google Analytics