Java为什么没有复制构造函数?


Answers:


134

Java。只是没有像在C ++中那样隐式地调用它们,我怀疑这是您的真正问题。

首先,复制构造函数无非是:

public class Blah {
  private int foo;

  public Blah() { } // public no-args constructor
  public Blah(Blah b) { foo = b.foo; }  // copy constructor
}

现在,C ++将使用以下语句隐式调用复制构造函数:

Blah b2 = b1;

在这种情况下,克隆/复制在Java中根本没有意义,因为所有b1和b2都是引用,而不是像C ++中那样的值对象。在C ++中,该语句复制对象的状态。在Java中,它只是复制引用。对象的状态不会被复制,因此隐式调用复制构造函数没有任何意义。

这就是全部。


19
+1。当我们其余的人对对象层次结构不屑一顾时,您便直接使用了语法-并且在这样做时可能回答了OP的真正问题。
丹·布雷斯劳

1
您可能要编辑分配;您将b2分配给它自己。同样,“政治家喜欢”在错误的地方有一个空格。
卡尔·马纳斯特

2
在这种情况下,您可能会说“ java can ”。
rogerdpack

4
如果Blah里面有非原始的东西怎么办?像:public class Blah { private A foo; //A is some class public Blah(Blah b) { foo = b.foo; } // this would not work would it ? }
Mr_and_Mrs_D 2011年

@Mr_and_Mrs_D您的示例将是一个浅表复制构造函数的实现,审慎的编码人员将这样记录它。它将按原样工作-Blah的新实例将共享对复制的Blah现有实例上相同A实例的引用。通过在A类定义中添加复制构造函数,然后在Blah中将该构造函数定义为public Blah(Blah b) { foo = new A(b.foo); }
RobertB,2014年

14

Bruce Eckel

为什么[复制构造函数]在C ++而不是Java中工作?

复制构造函数是C ++的基本组成部分,因为它会自动创建对象的本地副本。但是上面的示例证明了它不适用于Java。为什么?在Java中,我们操作的所有内容都是一个句柄,而在C ++中,您可以拥有类似句柄的实体,还可以直接传递对象。这就是C ++复制构造函数的目的:当您想要获取一个对象并按值传递它时,从而复制该对象。因此,它在C ++中可以正常工作,但是您应该记住,该方案在Java中会失败,因此请不要使用它。

(我建议阅读整个页面-实际上,请从此处开始。)


10

我认为答案很有趣。

首先,我相信在Java中所有对象都在堆上,虽然没有指针,但确实有“引用”。引用具有复制符号,并且Java在内部跟踪引用计数,以便其垃圾收集器知道可以安全删除的内容。

由于仅通过可复制的引用访问对象,因此大大减少了复制对象的实际次数(例如,在C ++中,仅将对象(按值)传递给函数会导致在Java中复制构造新对象。仅传递对对象的引用)。设计人员可能认为clone()对于其余用途就足够了。

 


2
我同意。复制构造函数确实正在解决C ++中的内存管理问题。
2009年

投票原因:* Java不使用复制语义(用于对象)。传递对象不会克隆或复制对象,也不会修改引用计数-只是传递引用。*复制语义与复制对该对象的引用之间的混淆太多。
Arafangion

在C ++中,您还应该通过指针或引用传递这些对象,以最大程度地减少多余的复制。这不是内存管理的问题,当您确实想复制对象的深层副本时,这只是语言中的(小的)语法差异。
迈克·卡勒

@Arafangion,难道不是Java不这样做的全部答案的一部分,而是复制了引用吗?无论如何,我还是+1
Johannes Schaub-litb

@Arafangion,这就是为什么Object.clone()存在。我也+1
Soutzikevich

2

这只是我的意见(我相信会有合理的答案)

当按值发送或返回类的实例时,C ++中的复制构造函数主要有用,因为这是透明激活复制构造函数的时间。

由于在Java中,所有内容都是通过引用返回的,而VM是面向动态分配的,因此对于复制构造函数的复杂性确实没有道理。

另外,由于所有内容都是参考,因此开发人员通常必须提供自己的实现以及如何克隆字段的决定。


1

猜猜他们认为您可以改用clone()方法吗?



0

我不是C ++程序员,但是我似乎确实记得关于“三个朋友”的规则-复制构造函数,赋值运算符和析构函数。如果您有一个,则可能需要全部三个。

因此,也许在语言中没有析构函数的情况下,他们不想包含复制构造函数吗?只是一个猜测。


不完全的。在C ++中,它更像是:如果您需要这三个之一(例如,复制构造函数),那么您很可能也需要另外两个,尽管您当时可能没有意识到。
丹·布雷斯劳

1
另外,如果不需要它们,则应将它们声明为私有的而不是实现它们。这将防止编译器替换其自己的“浅”复制版本...
dicroce

0

好吧,可以。只是不会隐式创建它。如果我不得不猜测,这可能与Java对象始终是堆分配的事实有关。

在C ++中,默认的副本构造函数是按成员的浅表副本。如果一个类拥有在堆上分配的内存(通过原始指针),这将导致副本与原始对象共享内部,这不是您想要的。

想象一下Java的行为。任何具有作为对象的字段的类(阅读:本质上都是它们)将具有错误的行为,并且您需要自己覆盖它。在99%的情况下,您没有为任何人省事。此外,您刚刚为自己创建了一个微妙的陷阱-想象一下您不小心忘记覆盖默认的副本构造函数。如果它是默认生成的,并且您尝试使用它,则编译器完全不会抱怨,但是您的程序在运行时会表现异常。

即使他们创建了执行深层复制的默认复制构造函数,但我不确定这样做是否特别有用。无论如何,您不仅会在Java中执行比C ++更少的副本,而且您并不总是希望深度复制一个字段。

您只是拥有的对象和由于需要但不负责的引用而持有的对象是相同的-只是字段。所有权和借款不是一流的概念。对于您拥有的对象,您想对其进行深层复制(除非它们是不可变的,在这种情况下您不应该打扰),而对于仅持有引用的对象,则需要复制该引用。

我会争辩说,一个复制构造函数,仅仅盲目地复制所有内容,也不适合许多类。当然,默认情况下,肯定比浅表复制要多。


-1

Java具有副本构造函数
注意:代替demo d2 = new demo(d1),您可以编写demo d2 = d1
主要区别黑白两个
demo d2 = new demo(d1)意味着创建了新对象并为其分配了内存,但
demo d2 = d1 表示仅创建引用变量,该引用变量使用对象d1的相同内存地址,因此d2未分配单独的内存。

复制构造函数的语法:
参见下面的示例首先,复制构造函数非常简单:))
classname(int datafield)//简单的构造函数
{
this.datafield = datafield;
}

classname(类名对象)
{
datafield = object.datafield; //参见下面的示例
}
现在可以调用
{

classname obj = new classname();

classname anotherObject = obj; //或classname anotherObject = new classname(obj)

}

 课堂示范
{
    私有int长度;

    私有int宽度;

    私有整数半径;

    演示(int x,int y)

    {
        长度= x;
        宽度= y;
    }
    int area()
    {
        返回长度*宽度;
    }

    //复制构造函数
    演示(演示obj)
    {
        长度= obj.length;
        广度= obj.breadth;
    }


    公共静态void main(String args [])
    {
        demo d1 =新demo(5,6);
        demo d2 = new demo(d1); //调用复制结构
        System.out.println(“ d1对象的面积=” + d1.area());
        System.out.println(“ d2对象的面积=” + d2.area());

    }
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.