【问题标题】:Circular dependency headache循环依赖头痛
【发布时间】:2012-09-30 10:45:39
【问题描述】:

最初的问题是 我有三个班级ABCA 有一个链接并调用B,它有一个链接并调用C。但是C 调用了A 静态方法。我就是无法让这个静态调用工作。

但是大家对我在截断代码时有些不准确表示不满,所以类的真实名称是 GameRendererGameViewFieldViewGameRenderer 有一个链接并调用GameView,它有一个链接并调用FieldView。但是FieldView 调用了GameRenderer 静态方法createGlTextureFromResource。我就是无法让这个静态调用工作。

这是完整且真实的标题 GameRenderer.h

// GameRenderer.h
#ifndef GAME_RENDERER
#define GAME_RENDERER

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

namespace game{
    class GameModel;
    class GameView;

    class GameRenderer {
            jobject*    context;
            jobject*    gameOverHandler;
            static JNIEnv*     env;
            static jobject*    jparent;

            GameModel*  gameModel;
            GameView*   gameView;

            glm::mat4 mProjMatrix;
            glm::mat4 mVMatrix;

        public:
            GameRenderer(jobject* context, jobject* gameOverHandler, JNIEnv* env, jobject* jparent);

            void onSurfaceCreated();
            void onDrawFrame();
            void onSurfaceChanged(int width, int height);
            void setMotionDirection();

            static int* getImagePixels (int imageId, int width, int height);
            static void createGlTextureFromResource (int resourceId, int textureId);
    };
}

#endif 

GameView.h

// GameView.h
#ifndef GAME_VIEW
#define GAME_VIEW

#include <glm/glm.hpp>

namespace game{
    class GameModel;
    class FieldView;

    class GameView {
        GameModel* gameModel;

        FieldView* fieldView;
        public:
        GameView(GameModel* gameModel);
        void draw(glm::mat4 mProjMatrix, glm::mat4 mVMatrix);
    };
}

#endif 

FieldView.h

// FieldView.h
#ifndef FIELD_VIEW
#define FIELD_VIEW

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

#include "GLESUtils.h"

namespace game{
    class GameRenderer;

    class FieldView {
        static const int FLOAT_SIZE_BYTES = 4;
        static const int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
        static const int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
        static const int TRIANGLE_VERTICES_DATA_UV_OFFSET = 3;

        glm::mat4 mMMatrix;
        glm::mat4 mMVPMatrix;

        int mProgram;
        int mTextureID;
        int muMVPMatrixHandle;
        int maPositionHandle;
        int maTextureHandle;

        public:
        FieldView();
        void draw(glm::mat4 mProjMatrix, glm::mat4 mVMatrix);
    };
}

#endif 

这里是 FieldView.cpp 的剪辑版

// FieldView.cpp
#include <jni.h>  
#include <string.h>  
#include <android/log.h>  

#include "FieldView.h"
#include "GameRenderer.h"

#define  LOG_TAG    "NDK_FieldView"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)

namespace game{
    static GLfloat mTriangleVerticesData[] = {...};

    static const char mVertexShader[] = "...\n";

    static const char mFragmentShader[] = "...\n";

    FieldView::FieldView() {
        mProgram            = -1;
        mTextureID          = -1;
        muMVPMatrixHandle   = -1;
        maPositionHandle    = -1;
        maTextureHandle     = -1;

        mProgram = GLESUtils::createProgram(mVertexShader, mFragmentShader);
        GLESUtils::checkGlError("createProgram mProgram");
        if (mProgram == 0) {
            LOGI("program not created exiting");
            return;
        }
        maPositionHandle = glGetAttribLocation(mProgram, "aPosition");
        GLESUtils::checkGlError("glGetAttribLocation aPosition");
        if (maPositionHandle == -1) {
            return;
        }
        maTextureHandle = glGetAttribLocation(mProgram, "aTextureCoord");
        GLESUtils::checkGlError("glGetAttribLocation aTextureCoord");
        if (maTextureHandle == -1) {
            return;
        }

        muMVPMatrixHandle = glGetUniformLocation(mProgram, "uMVPMatrix");
        GLESUtils::checkGlError("glGetUniformLocation uMVPMatrix");
        if (muMVPMatrixHandle == -1) {
            return;
        }

        /*
         * Create our texture. This has to be done each time the
         * surface is created.
         */

        GLuint textures[] = {0};
        glGenTextures(1, &textures[0]);

        mTextureID = textures[0];
        glBindTexture(GL_TEXTURE_2D, mTextureID);

        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

        // here is the actual problem, I get the following error on this line
        // FieldView.cpp: In constructor 'game::FieldView::FieldView()':
        // FieldView.cpp:89:9: error: incomplete type 'game::GameRenderer' used in nested name specifier

        GameRenderer::createGlTextureFromResource(0x7f040004, mTextureID);
    }

    void FieldView::draw(glm::mat4 mProjMatrix, glm::mat4 mVMatrix) {
        //... doesn't matter
    }
}

如果我理解正确,我必须使用前向声明,并且至少在一个地方不要使用包含头文件来阻止循环,但我不能这样做,因为我在对象之间进行了调用,并且没有头文件编译器拒绝调用方法。 编译器错误FieldView.cpp中,见GameRenderer::createGlTextureFromResource(0x7f040004, mTextureID);调用附近代码中的cmets。

在所有这些修改之后问题实际上保持不变: 我在这里做错了什么?我想我在某处使用了非常糟糕的设计实践。

请原谅我最初的错误。

【问题讨论】:

  • 我不太明白这个问题。你为什么不包括其他标题呢?另一方面,我错过了文件末尾的包含:-(
  • 我在头文件中包含了头文件,所以C a.k.a FieldView 中的名称出现了明显的重叠

标签: c++ header linker circular-dependency


【解决方案1】:

我认为这应该可行:

  • 停止包含来自其他标头的标头。您不需要它们,这从它仅包含在最后一行这一事实中可以清楚地看出。作为一般规则,在标头中包含尽可能少的内容,并尽可能避免使用简单的声明(例如 class B;)。

  • 包括来自C.cppA.hC.h。这两个类都直接从C.cpp 使用,因此您需要包含两个标头是有意义的。

【讨论】:

  • 嗯,我试过了,但没有效果,编译器说同样的错误......从逻辑上我看不出有什么不同。
  • 其他模块的include guards有错误...guard的名字和GameRenderer重合...开始逐个修改header include文件的模型的时候发现了,谢谢你的答案!
【解决方案2】:

至少在您发布的代码中,没有理由在A.h 的底部包含B.h 等等。您应该将这些包含在实现文件中。

// A.cpp
#include "A.h"
#include "B.h"
namespace game
{
 ....
}

【讨论】:

  • 这并没有改变任何东西,我仍然收到FieldView.cpp C.cpp: In constructor 'game::C::C()': C.cpp:89:9: error: incomplete type 'game::A' used in nested name specifier
  • @AntonBoritskiy 这没有多大帮助,因为您的示例没有任何名为 FieldView 的内容。你能发布一个有代表性的例子吗?
  • @AntonBoritskiy 我很困惑。正如我的回答所说,如果您将C(jobject* context); 更改为C();(因为您在 C.cpp 中的默认构造函数需要声明),它会为我编译。您实际上是在测试您在此处提供的代码,还是只是应该代表您实际使用的代码?
  • @AntonBoritskiy - 错误消息说问题出在 C.cpp 的第 89 行,上面提供的 C.cpp 只有 11 行。编译发布的代码时会发生什么?
  • 是的,抱歉,我在翻译名称时不准确,请参阅更新的问题以获取完整来源
【解决方案3】:

据我所知,#include "A.h" 在 C.h 头文件中没有给你买任何东西。您的 C.cpp 标头应包括 A.h 和 C.h。同样,B.h 也在尾部包含 C.h,就像以前一样,什么也不买。

A.h - include no other headers, forward decl class C;
B.h - include no other headers, forward decl class C;
C.h - include no other headers, forward decl class A

A.cpp - include A.h, C.h
B.cpp - include A.h, B.h, C.h
C.cpp - include A.h, C.h

看来您的口头禅是只包含一个 .cpp 文件的自定义标头。多类/多头层次结构几乎不会出现这种情况。

【讨论】:

    【解决方案4】:

    您的代码为我编译。尽管通过将 C 中的构造函数更改为默认构造函数(因此 C.cpp 中的代码具有声明)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-09-27
      • 2011-07-11
      • 2012-02-19
      • 1970-01-01
      • 1970-01-01
      • 2011-01-06
      • 2011-06-16
      • 2015-07-25
      相关资源
      最近更新 更多