【问题标题】:Why is the C++ compiler not detecting a properly declared Class?为什么 C++ 编译器没有检测到正确声明的类?
【发布时间】:2020-07-10 08:39:27
【问题描述】:

根据错误代码,编译器 (MSVC 2019) 未检测到正确声明的类。

在以下代码中,编译器在UACcDefault 类定义中第一次提及UACvLogin 时停止。

[\src\UAC\controllers\UACcDefault.h][1]:

#ifndef MANAGER_UACCDEFAULT_H
#define MANAGER_UACCDEFAULT_H

#include "src/includes.h"
#include "../views/UACvLogin.h"
#include "../models/UACmUsers.h"

//class UACvLogin;
//class UACmUsers;

class UACcDefault {
public:
    UACcDefault();
    UACcDefault(int iMaxLogin, UACmUsers* mUsers = nullptr, UACvLogin* viewLogin = nullptr);
    virtual ~UACcDefault();

    int GetUserid() const;
    void SetUserid(int userid);
    const wxString& GetUsername() const;
    void SetUsername(const wxString& username);
    const wxString& GetPassword() const;
    void SetPassword(const wxString& password);
    int GetContactid() const;
    void SetContactid(int contactid);

    bool GetLoggedIn();

protected:

private:
    UACmUsers* mUsers;
    UACvLogin* viewLogin;
    int iMaxLogin;

};

#endif //MANAGER_UACCDEFAULT_H

错误信息是:

src/UAC/controllers/UACcDefault.h(19): error C2061: syntax error: identifier 'UACvLogin'
src/UAC/controllers/UACcDefault.h(37): error C2143: syntax error: missing ';' before '*'
src/UAC/controllers/UACcDefault.h(37): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src/UAC/controllers/UACcDefault.h(37): error C2238: unexpected token(s) preceding ';'

我研究了代码,C2061C2143C4430C2238,它们表明编译器没有检测到 UACvLogin 之前已经声明过。

每当我采取措施缓解这些错误时,例如更改代码以使 UACvLogin 不是指针时,我都会收到以下错误消息:

src/UAC/controllers/UACcDefault.h(37): error C3646: 'viewLogin': unknown override specifier
src/UAC/controllers/UACcDefault.h(37): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int

编译器现在关注变量的名称 (viewLogin),而不是类型。如果我从头文件中删除 UACvLogin* viewLogin(该变量当前仅用于一种方法),或者取消注释 class UACvLogin; 行,编译器会将其焦点更改为 UACmUsers 标识符:

src/UAC/controllers/UACcDefault.h(36): error C2143: syntax error: missing ';' before '*'
src/UAC/controllers/UACcDefault.h(36): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
src/UAC/controllers/UACcDefault.h(36): error C2238: unexpected token(s) preceding ';'

如果我取消注释 both 类声明 class UACvLogin;class UACmUsers;,编译器会抱怨 SOCI 库。我不会在这里引用错误消息。

我使用/P 选项执行了构建以查看预处理器输出,并且包含将类声明放在它们应该在的位置:在UACcDefault 类中使用/引用它们之前。

谁能帮我找出这个错误的原因?


整个项目在GitHub

【问题讨论】:

  • UACvLogin.h 是否包含UACcDefault.h
  • 请尝试创建一个minimal reproducible example(强调minimal部分)。
  • 如果没有看到"../views/UACvLogin.h",就很难诊断问题。您说UACvLogin 已正确声明,但显然有问题。不得不同意 Nathan 这看起来有点像循环依赖。
  • 我查看了 github 存储库,发现与 "UACvLogin.h" 存在循环依赖关系,最终试图包含自身,这就是问题所在。
  • 如果您查看预处理器输出文件 (raw.githubusercontent.com/ptrongithub/Manager/development/…),UACvLogin.h 仅包含一次。每个头文件都有包含保护。

标签: c++ boost wxwidgets clion soci


【解决方案1】:

对于循环包含链,标头保护或类似功能无法按预期工作。问题是头文件A.h需要来自头文件B.h的符号x,而头文件B.h需要来自头文件A.h的符号y。两者都无法得到他们的意愿,一些头文件将使用另一个文件中的符号而没有实际包含其头文件(感谢包含保护)。


让我们做一个简单的例子,使用上面的名字......

第一个头文件A.h:

#ifndef A_H
#define A_H

#include "B.h"

struct x
{
    y y_member;
};

#endif

然后是头文件B.h:

#ifndef B_H
#define B_H

#include "A.h"

struct y
{
    x x_member;
};

#endif

现在如果我们通过预处理器运行头文件A.h,结果代码将是这样的:

// Included from B.h
struct y
{
    x x_member;
};

// Part of A.h itself
struct x
{
    y y_member;
};

由于标头包含保护,文件B.h 实际上不包含来自A.h 的任何内容,这意味着符号x 在使用时将是未知的。

如果您删除标头包含保护,那么您将拥有无限“递归”的包含,因此这也不起作用。


正如我在评论中提到的,您自己已经有了解决方案的开始。修改UACcDefault.h如下:

#ifndef MANAGER_UACCDEFAULT_H
#define MANAGER_UACCDEFAULT_H

#include "src/includes.h"

// Don't include these header files
// #include "../views/UACvLogin.h"
// #include "../models/UACmUsers.h"

// Do these forward declarations
class UACvLogin;
class UACmUsers;

class UACcDefault {
    // ... Cut to make it shorter
};

#endif //MANAGER_UACCDEFAULT_H

还要确保src/includes.h 文件不包含违规文件。

您还应该对 UACvLogin.hUACmUsers.h 文件进行类似的编辑。

【讨论】:

  • no 循环包含。 (预处理器可能包括,即#include,但不在类声明或类定义中。)UACmUsersUACvLogin 都不知道UACcDefaultUACcDefault 确实有 UACmUsersUACvLogin 类型的成员数据。这些类之外的所有代码都知道UACcDefault,但不知道其他两个类。
  • @JVines 请仔细看看我的例子,它显示了一个典型的循环依赖。您拥有的任何头文件是否在任何时候直接或间接包含其他文件之一? (通过*间接地,想想A.h 包含B.h 包含C.h 包含A.h 的情况,这是同一件事。)如果任何文件UACvLogin.hUACmUsers.h 头文件包含@ 987654355@你有循环依赖。
  • @JVines 你已经有了解决你自己问题的方法,这些前向声明被注释掉了。这些需要“注释”,并且您应该包含UACvLogin.hUACmUsers.h 头文件。这些头文件可能也不应该包含UACcDefault.h
  • 再看看你的例子。你有 class a 和成员 bclass b 和成员 a。这在我的代码中不会发生。我有班级UACcDefault,成员UACmUsersUACvLogin。类UACmUsers 没有成员UACcDefaultUACvLogin 并且类UACvLogin 没有成员UACcDefaultUACmUsersUACvLogin.hUACmUsers.h 的包含必须存在。 UACcDefault.h 是唯一引用这两个标头的文件(当然,cmakelists.txt 除外)。
  • @JVines 第一个例子只是一个例子。不,请定义指向某些结构的变量,您不需要完整的结构,只需要前向声明。
【解决方案2】:

通知#includes 来自includes.h,其中还包括main.h

includes.h 文件包含大部分代码所需的一切:wxWidgets、boost::propertytree、SOCI。由于需要访问一些全局变量(首选项),main.h 也包括在内。

我已将 includes.h 重构为每个库的单独文件。只有模型(我使用的是模型-视图-控制器架构)需要访问 SOCI。

我已将首选项数据和方法移动到具有静态成员的单独类中。这会清理 MyApp 并消除包含 main.h 的需要。我能够简化对每个首选项设置的访问以及读取和写入首选项数据文件。只有这个类需要访问 boost::propertytree。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-31
    • 2012-08-17
    • 2016-02-04
    • 2016-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多