C++ A C++;协方差/覆盖/循环问题

C++ A C++;协方差/覆盖/循环问题,c++,covariance,circular-dependency,overriding,C++,Covariance,Circular Dependency,Overriding,我正在编写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

我正在编写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;李> 后端将:

  • 尽可能使用远期申报
  • 否则将使用包括,检查后者是否为循环;如果是,它将失败并显示错误消息
  • 干杯,
    Artur

    当您通过机器生成代码时,可以使用一些肮脏的技巧

    class B;
    
    class A {
      public: virtual B* CLASS_A_get();
    }
    
    class C;
    
    class B : public A {
      public:
        virtual B* CLASS_A_get();
        virtual C* CLASS_B_get();
    }
    
    class C : public B {
    }
    
    // In B's .cpp file, you can include C.h
    #include "C.h"
    B* B::CLASS_A_get() {
        return CLASS_B_get();
    }
    

    这里有严重的设计问题

    为了使虚函数功能正常工作,类A、B和C中的所有get()函数都应该具有相同的签名

    将所有内容更改为:

    虚拟A*get()

    然后


    这就是你想要的吗?

    我不确定你对问题来源(编写编译器后端)的解释是否真实,因为(1)所提供的代码甚至不正确,没有分号,我认为编写编译器的人会设法提供正确的代码,(2)Java代码中不会自然出现问题,我不确定它是否可以直接用Java表达,除非我在下面介绍的解决方法(在这种情况下,你不需要问),并且(3)这不是一个困难的问题,也不是一个编译器编写者会遇到的问题

    也就是说,我强烈怀疑这是作业

    也就是说,您只需自己实现协方差,例如:

    class B;
    
    class A
    {
    private:
        virtual B* virtualGet() { ... }
    public:
        B* get() { return virtualGet(); }
    };
    
    class C;
    
    class B
        : public A
    {
    private:
        virtual B* virtualGet() { ... }
    public:
        C* get() { return static_cast<C*>( virtualGet() ); }
    };
    
    class C
        : public B
    {};
    
    B类;
    甲级
    {
    私人:
    虚拟B*virtualGet(){…}
    公众:
    B*get(){return virtualGet();}
    };
    丙级;;
    B类
    :公众A
    {
    私人:
    虚拟B*virtualGet(){…}
    公众:
    C*get(){return static_cast(virtualGet());}
    };
    C类
    :公共B
    {};
    
    <> P>这与实现协变SyrScor结果相同,除了StaskPosiple结果,可以更依赖C++支持协变原始指针结果。

    这是一项众所周知的技术


    干杯&hth.,

    您可以自己干净地实现协方差,无需依赖编译器,也无需任何强制转换。下面是一个包含一些循环依赖项的标题:

    class A
    {
      public:
        A* get() { return get_A(); }
      private:
        virtual A* get_A();
    };
    
    class B : public A
    {
      public:
        C* get() { return get_C(); }
      private:
        virtual A* get_A();
        virtual C* get_C();    
    };
    
    class C : public A
    {
      public:
        B* get() { return get_B(); }
      private:
        virtual A* get_A();
        virtual B* get_B();    
    };
    
    根据B::get_C实现B::get_A,根据C::get_B实现C::get_A。所有get_*都在cxx文件中实现。您可以这样做,因为这三个类都已完全定义。用户总是调用get()

    抱歉,如果格式错误,我是从手机发帖的


    编辑:使用静态强制转换的解决方案并不总是适用的,例如,当涉及虚拟继承时(您将需要动态强制转换)。

    这根本不能解决他的问题,因为派生类必须有新的签名。@Sharath正如我对另一张海报所说的,问题是关于实现编译器后端,与设计无关。不过,我不同意,一般来说,设计错误是一个设计错误,因为重写函数是一个子类型,因为它是java中的一个正常的实践。C++中,你想做的事情毫无意义。我已经十年没有使用Java了,所以我不能对Java发表评论。我在想一个大致相同的答案。但是当编译器需要调用一个
    get()
    方法时,它需要决定调用哪个方法。我认为这是一个可以解决的问题,前提是所有三个(或N个)类的头文件都在生成代码的范围内可见。@DeadMG这很聪明,但不幸的是,filt只能在实现文件中使用,当头文件将被其他程序员使用。@ AraTaj:你是说头文件会被人们自己编写C++代码使用吗?从java生成C++是很困难的,不想让它对程序员友好C++。无论如何,你不能这么做,因为没有办法将java的GC转换成C++的内存管理。Dan:生成最派生的调用-例如,如果您有一个B*,则调用类_B_get()。@Dan@DeadMG I使用finalize()模拟析构函数。Java程序员必须
    class A
    {
      public:
        A* get() { return get_A(); }
      private:
        virtual A* get_A();
    };
    
    class B : public A
    {
      public:
        C* get() { return get_C(); }
      private:
        virtual A* get_A();
        virtual C* get_C();    
    };
    
    class C : public A
    {
      public:
        B* get() { return get_B(); }
      private:
        virtual A* get_A();
        virtual B* get_B();    
    };