Java和C++中的对象数组有什么区别?

7
我的背景是C++,在C++中我们可以使用简单的语法创建对象数组。 className obj[n]; 并且构造函数将被调用n次。
但是当我尝试在Java中创建对象数组时,className[] obj = new className[n]; 没有调用构造函数。在搜索后,我在stackoverflow上找到了这个问题的答案,它只创建可以指向n个对象的n个引用,我需要再为每个引用创建对象,如下所示:obj [0] = new className(); 现在我想问为什么Java会这样做?即使C++允许,但Java不允许以同样的方式创建对象数组吗? 我搜索了这个问题,但仍然没有得到确切的答案。
4个回答

9
在C++中,您有灵活性选择创建对象的内存。您可以在自动区域(堆栈上),静态区域或动态区域中创建对象。在最后一种情况下,您会得到一个对象指针,并在完成后负责释放它。
相比之下,Java只有动态区域选项,因为所有对象都是引用对象。在C++中,这相当于仅通过指针使用对象,并始终使用new创建它们。当您在C++中执行此操作时,还必须使用new填充指针数组中的对象:
myclass *array[10];
for (int i = 0 ; i != 10 ; i++) {
    array[i] = new myclass();
}
...
for (int i = 0 ; i != 10 ; i++) {
    delete array[i];
}

允许在C++中创建对象数组是出于需要让程序员在自动区域中分配对象数组的选择。这种选择带来了一些折衷,因为用于创建数组的对象必须具有默认构造函数。这并不理想,因为要求默认构造函数听起来很武断。

另一方面,Java没有自动内存需求的限制,因此他们采用了一个简单的解决方案,需要你逐个初始化对象。


3

创建与数组相同类型的对象,通常不需要使用默认构造函数。有时您想要调用自定义构造函数。有时您想要实例化子类并将它们存储在数组中。

请注意,Java 数组 className[] obj 更等同于 C++ 数组 className* obj[n],而不仅仅是 className obj[n],因为它是对象引用的数组,而不是对象本身的数组。截至 Java-8,您不能创建对象本身的数组(它们作为 Valhalla 项目 的一部分进行了讨论,但甚至不会出现在 Java-9 中)。当对象本身存储在 C++ 数组中时,您必须 初始化该数组。您不能在其中保留 "null" 或类似内容,因为 null 不是对象,而是引用(或指针)。当您在 C++ 中创建 className* obj[n] 数组时(它更类似于 Java className[] obj 数组),它也是未初始化的。

最后请注意,在 Java-8 中,您可以通过以下方式很容易地创建所有对象,即使用默认构造函数进行实例化:

className[] array = Stream.generate(className::new).limit(n).toArray(className[]::new);

@Tiger 如果我们需要一些无参构造函数和有参构造函数,那么为什么要创建引用呢?我们可以在需要不同构造函数时创建对象。例如:className obj=new className(5); 或者 className obj2=new className("A"); 我认为这只是为了代码可读性和减少编码量。还有其他原因吗?(抱歉我的英语不好) - user5028722
@Tagir 当然这不是你的错,但我不会认为Java-8的解决方案是“容易”的。 - Manos Nikolaidis
@LetDoit,抱歉我不理解你的问题。请尝试重新表述一下。如果评论区不够,请考虑发布一个新问题,并添加所有必要的示例。 - Tagir Valeev
@TagirValeev,作为您答案的第一行,我们可以使用不同的构造函数创建不同的对象,但为什么要选择引用数组呢? - user5028722
1
@LetDoit,你可能想要创建一个类似于className[] obj = {new className(0), new className(1), new className(2), new childClassName(3)};的数组,以不同的方式初始化所有的数组元素。但是,如果我们想在创建后统一处理它们,将它们存储在单独的变量中可能是不合适的。 - Tagir Valeev
@TagirValeev 谢谢您。 - user5028722

2

什么是允许或不允许的由语言设计者决定。

如果您想在Java中使用对同一对象的引用来初始化数组的所有元素,您可以使用:

className[] obj = new clasName[2];
Arrays.fill(obj, new className());

或者创建不同的对象并向每个构造函数传递不同的参数 className[] obj = new className[] {new className(), new className()};

那么为什么我们选择不同的编程语言呢?这是因为每种语言都有性能、安全等方面的差异,每个语言的设计者都会出于某种原因选择语法/逻辑。抱歉我的英语不好。-_- - user5028722
1
请注意,Arrays.fill解决方案将创建一个对象并将其分配给每个数组元素。这样的结果很少被期望。通常,您需要为每个数组元素提供不同的对象。 - Tagir Valeev
那么为什么我们选择不同的编程语言呢?原因在于每种语言的性能差异。然而,通常选择一种语言并不完全是出于性能考虑。例如,你正在开发一个复杂的构建系统,它是多模块的,并且有许多自定义要求。你最终选择了Gradle/Groovy,因为它是这项工作的正确工具......而绝对不是因为它的性能。 - scottb
@LetDoit 简洁的语法并不是 Java 的强项。相反,Java 比 C++ 更加高效,这是因为有许多可用的库和我从我的 IDE(Intellij)获得的支持。 - Manos Nikolaidis
@scottb 我的评论问题和你的回答是一样的,只是因为我的英语不好。无论如何,我明白了原因。谢谢。 - user5028722
@ManosNikolaidis 谢谢,抱歉我在问题中忘记提到“我知道解决方案但不知道原因”。 - user5028722

1

在Java中,无论是对象成员还是局部变量,每当您声明变量时,它都是原始类型(byte, char...)或引用类型(指向某种类型的对象的指针)。因此,没有对象数组,只有引用数组。

在C++中,您有自由和责任选择需要进行多少间接引用,如何分配、释放、构造和销毁对象,并增加了很多复杂性:

  • 取消引用运算符 (*pointer)
  • 取消引用并使用成员 (pointer->member)
  • 地址运算符 (&object)
  • 从其他类型派生指向类型的指针的方法 (type* var)。
  • 放置new (`new(pointer) type(arg1, arg2);
  • 显式删除操作符 (delete pointer)
  • 等等。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接