【发布时间】:2011-07-06 15:44:09
【问题描述】:
我正在编写 Java 子集的编译器的后端。后端编写 C++ 代码。不过,有一些假设的 Java 代码,我不知道如何翻译成 C++。
以下代码显示了一个示例问题。 A由B扩展,B由C扩展,下面分别是三个头文件A.h、B.h和C.h:
#ifndef A_H
#define A_H
class B;
class A {
public: virtual B* get();
}
#endif /* !defined(A_H) */
==========================
#ifndef B_H
#define B_H
#include "A.h"
class C;
class B : public A {
public: virtual C* get();
}
#endif /* !defined(B_H) */
==========================
#ifndef C_H
#define C_H
#include "B.h"
class C : public B {
}
#endif /* !defined(C_H) */
可以看出,B 覆盖了 A 的方法 get()。重写方法返回一个指向相应子类的指针,我猜这在 C++ 中是有效的,这要归功于协方差。
我不知道的是通知编译器的方式,C 确实是 B 的子类,因此,覆盖方法是有效的。
正如在代码中看到的那样,在 B.h 中对 C 进行前向声明是不够的,因为它没有说明 C 的超类。
在 B.h 中包含 C.h 将是循环的,因为 C.h 已经包含 B.h。后者是必需的,因为只有超类的前向声明是不够的。
可以用它做什么?
编辑两句话。
1 一位发帖人声称,以下内容在 Java 中是不可能的,所以我添加了一个 Java 版本:
A.java:
class A {
public B get() {
return null;
}
}
B.java:
class B extends A {
public C get() {
return null;
}
}
C.java:
class C extends B {
}
它编译得很好。
2 我不坚持编译这种有些奇怪的案例。如果它们不能被翻译成 C++ 中的可读代码,那么很好,后端将失败并显示错误消息。事实上,我对解决循环依赖的一般方法更感兴趣,比如在 C++ 中。
编辑 2
谢谢大家,这个网站的效率给我留下了深刻的印象。
我的结论是,因为:
- 生成的头文件将被其他程序员使用;
- 根据您的回答猜测,可能没有解决方案可以生成简单、可读的头文件;
- 涉及返回类型的循环引用可能很少见;
- 我避免使用 C++,因为除其他外,它允许这样的解决方案—— 我知道 C++ 有它的用途,但就我个人而言,我更喜欢语法更简单的语言,比如 Java;
后端将:
- 尽可能使用前向声明;
- 否则它将使用包含,检查后者是否是循环的;如果是,它将失败并显示错误消息。
干杯, 阿图尔
【问题讨论】:
-
旁注:我认为您无法在 C++ 中提取具体示例。要使协变起作用,编译器必须知道类型实际上是协变的,这就要求在
B类的定义中存在C定义(如果不存在,编译器无法知道它实际上是否存在)协变或只是不相关),但是,对于要定义的C(并且因为它依赖于作为基础的B)B必须事先定义。虽然前向声明可以打破一些循环依赖关系,但协变返回类型不能这样做。 -
c++ 和 java 的根本区别在于 c++ 支持单遍编译器,因此需要前向声明并在翻译单元中以正确的顺序放置类。仅当您的依赖关系树由于循环等原因更改为依赖关系图时才需要前向 decls。这与 java 处理它的方式非常不同。
-
j2sdk 1.4 Java 编译器可能已经很老了,但它拒绝编译您的 Java 代码,说“b.java:2: get() in B cannot override get() in A;试图使用不兼容的返回类型”。你写了“其中一张海报”,我认为这意味着我,即使我没有声称你归因于未指明的人。请更加小心,更加准确,不要做出误导性陈述和虚假声明。
-
@Alf 我不想冒犯你。您确实只是提出了一个假设,但我承认错误地将其解读为一种说法,因为“在 Java 中不会自然发生”。而且,您给了我一个有用的答案,但是由于某种原因,一旦我“检查”了另一个答案,它就“未检查”。 Java 5 引入了返回类型的协变。
-
@arataj:谢谢,我不知道;显然,我需要复习 Java!
标签: c++ covariance circular-dependency overriding