【问题标题】:What is the purpose of copying an object in object oriented programming?在面向对象编程中复制对象的目的是什么?
【发布时间】:2023-03-31 05:08:02
【问题描述】:

我已经学习 c# 大约 3 个月了,我今天遇到的一件事是对象上的深浅复制构造函数的概念(刚刚习惯了基类、继承和实例化的概念——多态性老实说,还没有真正沉入其中……我离题了)。

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

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

【问题讨论】:

  • 我建议仅在模板类的情况下使用克隆,而不是那些基本上继承 bcos class 的类可能在 constructor 中具有不同的事件触发器并且具有抽象方法/属性编号应该记住的一件事。 Serialization 也是克隆任何引用值类型对象时通常考虑的另一个因素。

标签: oop object constructor deep-copy


【解决方案1】:

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

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

  • 因为逻辑需要它:当国际象棋算法想要进行下一步时,它可能会在内部制作当前棋盘的许多副本,并用许多可能的棋步中的一种来修改它们中的每一个它可能会使用一些启发式方法制作、评估每个新板,选择最好的,然后将其用作新的当前板。

  • 防御性:Person 对象被要求提供其DateOfBirth 时,它不一定会返回对其自己的DateOfBirth 的引用,因为随后有人可能会更改@987654324 @对象,从而改变了人的DateOfBirth。因此,Person 对象可能会返回其DateOfBirth防御性副本

  • 拍摄快照:如果我有要调用的事件观察者列表,我可能想在开始之前拍摄列表的(浅)快照副本调用观察者,因为观察者可能决定在我处理列表时注销自己,这将产生灾难性的后果。 (ConcurrentModificationException,查一下。)

【讨论】:

    【解决方案2】:

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

    Whatever a = new Whatever();
    
    Whatever b = a;
    
    a.myField = "stuff";
    

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

    但是,如果您要创建一个想要独立维护的原型对象,则需要将字段复制到深层副本中。

    例子

    Car priusPrototype = new Car("Toyota", "myModel"); //etc.
    
    Car myPrius = priusPrototype.clone();
    Car neighboursPrius = priusPrototype.clone();
    
    myPrius.regNumber ="AB14 33ND";
    

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

    这是一个人为的例子,你可能会在这种情况下使用工厂,但这是一个例子,说明你可以拥有几乎相同字段的东西,但你需要保持独立性.

    【讨论】:

      【解决方案3】:

      如果我有一个 person 类,我肯定会创建一个 person 类的新实例吗?

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

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

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

      【讨论】:

        【解决方案4】:

        复制构造函数是一个特殊的构造函数,它从现有对象初始化一个新对象。编译器将创建一个默认的复制构造函数,如果你不创建它,它会一点一点地复制你的类数据。那么为什么我们必须重新创建复制构造函数?考虑这个类(不是专业代码)。

        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;
        };
        

        但是有一个大问题。当 main 要死时,它会清除所有堆栈变量。所以可能首先会调用“a”析构函数并删除“ap”。接下来会调用 b 析构函数,这再次尝试删除已经在 a 中删除的 bp(注意 thst ap 和 bp 指向相同的指针)。这将导致运行时错误。为了克服这个问题,我们必须创建一个副本 aonstructor 和 Assignement 运算符(在不同的场景)。所以A类复制构造函数将是, 折叠 |复制代码

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

        所以现在上面例子中的a、b都会有不同的p指针。赋值运算符的功能也是一样的。但是目标不同。

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

        • 当实例化一个对象并使用以下值初始化它时 另一个对象(如上例所示)。

        • 当传递一个对象时 价值。

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

        在其他情况下,例如,

        A a;
        a = b; 
        

        调用赋值运算符

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

        【讨论】:

          【解决方案5】:

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

          浅拷贝是对原始实例的唯一引用的副本。所以基本上,处理副本你仍然在处理相同的原始实例,在不同的引用之间共享。在某些情况下,当您需要共享单个对象,或者当您有一个复杂的对象很难并且“破坏性能”到 deep copy 时,这可能很有用。但是在某些情况下共享实例可能很危险,并且通常需要额外的工作来设计线程安全的类。

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2012-01-27
            • 2012-02-24
            • 1970-01-01
            • 2014-09-04
            • 2011-02-21
            • 1970-01-01
            • 2011-01-11
            • 1970-01-01
            相关资源
            最近更新 更多