当我们有深拷贝和浅拷贝时为什么懒复制?

时间:2011-10-08 16:48:56

标签: c++

我们有浅拷贝深拷贝,当我们想要在C ++中复制对象时,他们可以为我们完成这项工作。所以,
什么是懒惰副本
这是程序员或编译器自己做的事情吗? 什么是懒惰副本有利的编程方案?

5 个答案:

答案 0 :(得分:5)

  

什么是懒惰副本?

Wikipedia恰如其分地定义。

惰性副本是浅拷贝和深拷贝的组合。最初复制对象时,使用(快)浅拷贝。计数器还用于跟踪共享数据的对象数。当程序想要修改对象时,它可以确定数据是否被共享(通过检查计数器)并且可以在必要时进行深层复制。 懒惰副本只是作为深层副本向外看,但尽可能利用浅副本的速度。由于反击,下行相当高但基本成本不变。此外,在某些情况下,循环引用也可能导致问题。

  

这是程序员或编译器自己做的事情吗?

程序员必须为自己的类实现此行为 编译器默认情况下在复制函数(复制构造函数和赋值运算符)中执行浅拷贝 Deep Copy是程序员必须为他的类实现的,因此成员(指针)的特殊处理可以用于复制函数。

  

懒惰副本有利于哪些编程方案?

理想地,
复制对象会导致性能损失但是对象没有经常被修改的情况,Lazy副本在性能方面会有利。

维基百科引用number of examples,其中使用了懒惰复制(写入时复制)。

答案 1 :(得分:1)

懒惰副本大致是:

  • 立即执行浅拷贝
  • 但稍后执行深拷贝,只有当它变得绝对必要时(即当对象即将被修改时),希望这一刻永远不会到来。

所以他们是不同的概念。惰性拷贝本质上是一个运行时优化,而浅/深拷贝是一个编译时构造,可用于实现惰性拷贝,但也可以单独使用。

答案 2 :(得分:0)

我不确定你的意思是“拥有浅薄而深刻的副本谁能为我们做这份工作”。在编写复制构造函数和赋值运算符时,您需要决定如何复制。

在浅拷贝中,您只需直接复制对象成员。如果这些成员是堆内存的指针,则副本指向堆内存的同一块。如果您不提供复制构造函数,这是C ++执行的默认复制。

在深层副本中,成员指针指向的任何内存本身都被复制(也可能在这些对象的成员上递归)。

在懒惰的副本中,你从一个浅的副本开始。如果对象或其子对象从未被修改过,那么你没事。无需制作堆内存的第二个副本。当两个副本中的任何一个被修改时,首先执行深度复制,以便不对这两个对象应用修改。这也称为写时复制。

延迟复制的目的是获得深拷贝的外观,具有浅拷贝的一些性能优势。这最终是否会带来性能提升取决于对象的使用情况。如果您计划不修改对象的许多副本,那么您可能会看到优势。如果大多数对象最终都会被修改,那么优势就会消失。当对象经常被修改时,在修改对象之前检查已经执行了深层复制的开销使得它比仅仅是普通的深层复制更糟糕。

字符串通常被认为是懒惰复制的好地方,因为很多时候,复制只是为了在不同的地方显示,但大多数字符串无论如何都使用深拷贝,或者使用浅拷贝,并且完全不允许修改。

答案 3 :(得分:0)

可能通常需要Deep Copy但未确定是否真的有必要时,会使用

Lazy Copy。 Deep Copy通常是一项昂贵的操作。如果您在100%的情况下无条件地执行此操作,然后发现您只需要10%的对象,那么浪费复制其他90%对象的工作就会浪费掉。

这是Lazy Copy的用法.Lazy Copy是推迟延迟按需版本的Deep Copy。使用Lazy Copy,您不会立即执行Deep Copy。相反,您通过将所有相关信息存储在收件人对象中(大部分时间归结为浅层复制)并等待,准备(或计划)深层复制直到知道深度复制是否真的对于该特定对象是必要的。如果结果是必要的,则执行实际的深层复制。如果不需要Deep Copy,那么它就不会发生,从而节省了你的工作。

答案 4 :(得分:0)

让我谈谈C ++

C ++中编写类的一个非常重要的部分是实现复制构造函数和overloaded = operator函数。

本教程讲述了编写这些函数的必要性,以使您的程序更有效。在进入概念之前,请先了解基本术语。

构造函数:它是一个在创建类的对象时调用的特殊函数。理想情况下,构造函数必须包含初始化类的数据成员的逻辑。

复制构造函数:在创建时初始化对象时调用它。调用复制构造函数时存在更多场景。

运算符函数:C ++允许在operator关键字的帮助下重载运算符。这有助于我们将用户定义的类型视为基本类型。

默认的复制构造函数和=运算符函数,由编译器插入,因为它们在类中缺失。它执行位模式复制,即只是将一个对象的数据成员复制到另一个对象中。

请考虑以下代码示例

    class CSample
 {        
     int x;
   public:
     //dafualt constructor
      CSample(): x(0){}          
      int GetX()
       {
      return x;
       }
  }; 

int main()
{
    CSample ob1; //Default constructor is called.
    CSample ob2 = ob1; //default copy constructor called.

    CSample ob3; //Default constructor called.
    ob3 = ob1; //default overloaded = operator function called.

}

在上面的例子中

CSample ob2 = ob1; 
//This line will copy the bit pattern of ob1 in ob2 so the data member 
// x in both the object will contain same value i.e 0.

同样声明

 ob3 = ob1;
 //This line will copy the bit pattern of ob1 in ob2 so the data member
 // x in both the object will contain same value i.e 0.

上面的代码将按预期工作,直到类成员未分配任何资源(文件或内存)。考虑一个类更新如下的场景。

     class CSample
    {
       int *x;
       int N;
     public:
       //dafualt constructor
        CSample(): x(NULL){}          
       void AllocateX(int N)
       {
          this->N = N;
          x = new int[this->N]; 
        }
        int GetX()
       {
         return x;
       }
       ~CSample()
       {
         delete []x;
        }
    };

   int main()
  {
    CSample ob1; //Default constructor is called.
    ob1.AllocateX(10);

    //problem with this line
    CSample ob2 = ob1; //default copy constructor called.

    CSample ob3; //Default constructor called.

    //problem with this line
    ob3 = ob1; //default overloaded = operator function called.
   }

    class CSample
  {
     int *x;
     int N; 
 public:
    //dafualt constructor
    CSample(): x(NULL)
    {}          
    //copy constructor with deep copy
    CSample(const CSample &ob)
  {
       this->N = ob.N:
       this->x = new int[this->N];
   }
        //=operator function with deep copy.
    void operator=(const CSample &ob)
    {
       this->N = ob.N:
       this->x = new int[this->N];

    }

    void AllocateX(int N)
    {
       this->N = N;
        x = new int[this->N]; 
     }
    int GetX()
   {
     return x;
   }
   ~CSample()
   {
    delete []x;
   }
 };