【发布时间】:2010-10-24 02:08:31
【问题描述】:
为什么 Java 不支持 C++ 中的复制构造函数?
【问题讨论】:
-
这些是一些很好的解释,谢谢大家!
-
另请阅读“复制构造函数有什么问题?为什么要使用 Clonable 接口?” stackoverflow.com/questions/388304/…
标签: java copy-constructor
为什么 Java 不支持 C++ 中的复制构造函数?
【问题讨论】:
标签: java copy-constructor
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 中,它只是简单地复制 reference。对象的状态没有被复制,所以隐式调用复制构造函数是没有意义的。
这就是它的全部内容。
【讨论】:
public class Blah { private A foo; //A is some class public Blah(Blah b) { foo = b.foo; } // this would not work would it ? }
public Blah(Blah b) { foo = new A(b.foo); }
来自Bruce Eckel:
为什么[复制构造函数]在 C++ 而不是 Java 中工作?
复制构造函数是一个基础 C++ 的一部分,因为它自动 制作对象的本地副本。然而 上面的例子证明它确实 不适用于Java。为什么?在 Java 中 我们操纵的一切都是 处理,而在 C++ 中你可以拥有 类似句柄的实体,你也可以 直接绕过物体。 这就是 C++ 复制构造函数 适用于:当你想采取 对象并按值传递它,因此 复制对象。所以它有效 在 C++ 中很好,但你应该保留 请注意,该方案在 Java 中失败, 所以不要使用它。
(我建议阅读整个页面——实际上,请改用here。)
【讨论】:
我认为这个问题的答案很有趣。
首先,我相信在 Java 中所有对象都在堆上,虽然您没有指针,但您确实有“引用”。引用具有复制语义,Java 在内部跟踪引用计数,以便其垃圾收集器知道可以安全地删除什么。
由于您仅通过可复制引用访问对象,因此您需要复制对象的实际次数大大减少(例如,在 C++ 中,仅将对象传递给函数(按值)会导致新对象被复制构造,在 Java 中只传递对对象的引用)。设计者可能认为 clone() 足以满足其余用途。
【讨论】:
Object.clone() 存在的原因。我也为我 +1
这只是我的意见(我相信有一个合理的答案)
当您按值发送或返回类的实例时,C++ 中的复制构造函数主要有用,因为此时复制构造函数被透明地激活。
由于在 Java 中所有内容都是通过引用返回的,并且 VM 面向动态分配,因此复制构造函数的复杂性确实没有理由。
此外,由于所有内容都是通过引用进行的,因此开发人员通常必须提供自己的实现并决定如何克隆字段。
【讨论】:
你猜他们认为你可以创建一个 clone() 方法来代替?
【讨论】:
确实如此。当浅拷贝没问题时,你有 [clone()](http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#clone()),当它们不是时,你必须像 C++ 一样实现深拷贝。
唯一的实质性区别是它是工厂方法而不是构造函数,但就灵活性和可测试性而言,这可能是一件好事。
【讨论】:
我不是一个 C++ 程序员,但我似乎记得关于“三个朋友”的规则 - 复制构造函数、赋值运算符和析构函数。如果你有一个,那么你可能需要全部三个。
所以也许语言中没有析构函数,他们不想包含复制构造函数?只是猜测。
【讨论】:
嗯,可以。它只是不会被隐式创建。如果我不得不猜测,这可能与 Java 对象总是堆分配的事实有关。
在 C++ 中,默认的复制构造函数是成员级别的浅拷贝。如果一个类拥有在堆上分配的内存(通过原始指针),这将导致副本与原始类共享内部,这不是您想要的。
想象一下Java有这种行为。任何具有对象字段的类(阅读:基本上所有对象)都会有错误的行为,您需要自己覆盖它。对于 99% 的情况,您没有为任何人省去任何麻烦。此外,您刚刚为自己创建了一个微妙的陷阱——假设您不小心忘记了覆盖默认的复制构造函数。如果它是默认生成的,并且您尝试使用它,编译器根本不会抱怨,但您的程序会在运行时出现异常。
即使他们创建了一个执行深度复制的默认复制构造函数,我也不确定这是否特别有用。无论如何,您不仅倾向于在 Java 中执行比 C++ 更少的副本,而且您并不总是希望对字段进行深度复制。
您刚刚拥有的对象,以及您持有引用的对象,因为您需要它们,但不负责,它们是相同的 - 只是字段。所有权和借款不是一流的概念。对于您拥有的对象,您需要深度复制它们(除非它们是不可变的,在这种情况下您不应该打扰),而对于您只持有引用的对象,您需要复制引用。
我会争辩说,只是盲目地深度复制所有内容的复制构造函数也不适合许多类。当然,这不仅仅是默认的浅拷贝。
【讨论】:
Java 有复制构造函数
注意:demo d2=new demo(d1)可以写成demo d2=d1
主要区别 b/w 两个
demo d2=new demo(d1) 表示创建了新对象并且它是
分配的内存但是
demo d2=d1 意味着只创建了引用变量
它使用对象 d1 的相同内存地址,因此未分配 d2
分离的记忆。
复制构造函数的语法:
见下文示例第一个复制构造函数非常简单:))
classname(int datafield) //简单构造函数
{
this.datafield=datafield;
}
类名(类名对象)
{
datafield=object.datafield;//见下例
}
现在打电话
{
类名obj=新类名();
classname anotherObject=obj;//或classname anotherObject=new classname(obj)
}
课堂演示 { 私有 int 长度; 私有宽度; 私有int半径; 演示(int x,int y) { 长度=x; 宽度=y; } 整数区域() { 返回长度*宽度; } //复制构造函数 演示(演示对象) { 长度=obj.长度; 宽度=obj.宽度; } 公共静态无效主(字符串参数 []) { 演示 d1=新演示(5,6); demo d2=new demo(d1);//调用复制结构 System.out.println("d1 对象的区域="+d1.area()); System.out.println("d2 对象的区域="+d2.area()); } }【讨论】: