在面向对象编程中复制对象的目的是什么?

What is the purpose of copying an object in object oriented programming?

我已经学习 c# 大约 3 个月了,今天我遇到的一件事是对象的深拷贝构造函数和浅拷贝构造函数的概念(刚刚习惯了 base class es,继承和实例化——老实说,多态性还没有真正深入人心……我离题了)。

当我在基础 class(在本例中,用户 class)上看到一个深度复制构造函数时,我的第一个想法是“你到底为什么要复制一个对象?” .我看过的文章解释了如何做到这一点以及它是如何工作的,但我仍然无法找到任何实际示例来说明 为什么要这样做 。如果我有一个 class 的人,我肯定会创建一个新的人实例 class?

我很感激这里可能缺少一些基本的东西,但如果有人能填补空白,那就太好了。一个真实的例子来证明它的用处会更好!干杯!

在很多情况下您可能需要复制对象。

  • 为方便起见:创建对象时,通常需要对其进行初始化,可以是调用方法设置值的形式,也可以是在向构造函数传递参数的形式。有时,执行所有这些初始化可能需要大量工作。如果您想要一个新对象 B,它与另一个对象 A 仅在一个值上不同,那么将 A 的副本作为 B 并更改 B 的单个值可能更容易,而不是从头开始创建 B。

  • 因为逻辑要求:当下棋算法要走下一步时,它可能会在内部复制当前棋盘,修改每一个其中之一可能采取许多可能的行动之一,使用一些启发式方法评估每个新板,选择最好的,然后将其用作新的当前板。

  • 防御:当一个Person对象被要求提供它的DateOfBirth时,它不一定return 对其自身 DateOfBirth 的引用,因为随后有人可能会更改 Date 对象,从而更改 Person 的 DateOfBirth。因此,Person 对象很可能 return 其 DateOfBirth.

    防御副本
  • 拍摄快照: 如果我有一个要调用的事件观察者列表,我可能想拍一张(浅)在开始调用观察者之前对列表进行快照复制,因为观察者可能会在我处理列表时决定注销自己,这将带来灾难性的后果。 (ConcurrentModificationException,查一查。)

If I had a class of person, surely I’d just create a new instance of the person class?

如果您希望 Person 的第二个实例也具有相同的名称、相同的年龄、相同的一切怎么办?

复制对象以得到表示相同状态(但彼此独立)的两个实例的过程称为克隆或复制。

在某些情况下,您可以通过创建一个新实例并调用所有必要的设置器来手动执行此操作,但有时它们并没有全部暴露在 public 接口中。无论哪种方式,它都是一个副本。

如果您只是通过引用复制一个对象,您会得到:

Whatever a = new Whatever();

Whatever b = a;

a.myField = "stuff";

你最终得到 b.myField 也包含 "stuff"。这是因为它是完全相同的对象。

但是,如果您正在创建想要独立维护的原型对象,那么您需要在深层副本中复制字段。

例子

Car priusPrototype = new Car("Toyota", "myModel"); //etc.

Car myPrius = priusPrototype.clone();
Car neighboursPrius = priusPrototype.clone();

myPrius.regNumber ="AB14 33ND";

邻居 Prius 没有改变,因为 myPrius 是一个单独的对象。

这是一个人为的例子,你可能会在这个例子中使用一个工厂,但它是一个例子,你会有 almost 相同的字段,但是你需要保持独立。

复制构造函数是一种特殊的构造函数,它从现有的对象初始化一个新对象 object.Compiler 将创建一个默认的复制构造函数,它会一点一点地复制您的 class 数据,如果您不创建 it.Then 为什么我们必须重新创建一个 Copy 构造函数?考虑这个 class(不是专业代码)。

Class A
{
    public int *p;
    A(){ p = new int;}
    ~A(){
         delete p;
     }
};
int main()
{
   A a;
   A b = a; //default copy constructor provided by compiler,which exactly copies a.p pointer to to b.p;
   return 0;
};

但是有一个大的 problem.when main 会死掉,它会清理所有堆栈 variables.So 可能首先 "a" 析构函数将被调用 "a.p" 是deleted.Next b 析构函数将被调用,它再次尝试删除已在 a 中删除的 b.p(注意 a.p 和 b.p 指向相同的指针)。这将导致运行 时间 error.To 克服这个问题我们必须创建一个复制构造函数和赋值运算符(在不同的场景中调用)。所以 Class 复制构造函数将是, 收起 |复制代码

class A::A(const class A& RefA)
{
   p = new int;    // create a new p 
   *p = *RefA.p; // copy the value
}

所以现在上面例子中的 a,b 都会有不同的 p pointers.Assignement 运算符功能也是 same.But 目的是不同的。

调用复制构造函数而不是赋值运算符的一般情况有以下三种:

  • 当实例化一个对象并用来自 另一个对象(如上例所示)。

  • 路过物体时 价值。

  • 当对象按值从函数返回时。

在其他情况下,

A a;
a = b; 

调用了一个赋值运算符

答案归功于వేంకటనారాయణ(venkatmakam)

首先你要清楚浅拷贝和深拷贝的区别

浅拷贝是对原始实例的唯一引用的副本。所以基本上,在副本上工作你仍然在同一个原始实例上工作,在不同的引用之间共享。这在某些情况下很有用,当您需要共享单个对象时,或者当您有一个复杂的对象并且难以 "performance-killing" 进行深度复制时。但是在某些情况下共享实例可能很危险,并且通常需要额外的工作来设计线程安全 类.

深度复制 而是生成一个新实例。所以你有多个对象(而不是对同一对象的不同引用)具有相同的状态,但独立生活。所以在这种情况下,更改复制实例的状态不会反映原始对象的状态。

深拷贝也是Prototype创建模式的基础。