4

Wt 建议使用前向声明来避免循环依赖。

// Settings.h
#include <Wt/Dbo/Dbo.h>
#include <string>

class User; // Forward declaration of User Wt::Dbo object

class Settings 
{
public:
  Wt::Dbo::ptr<User> user;

  template<class Action>
  void persist(Action& a)
  {
    Wt::Dbo::belongsTo(a, user);
  }
};

 

// User.h
#include <Wt/Dbo/Dbo.h>
#include <string>

#include "Settings.h"

class User
{
public:
  Wt::Dbo::weak_ptr<Settings> settings;

  template<class Action>
  void persist(Action& a)
  {
    Wt::Dbo::hasOne(a, settings);
  }
};

但是,当我Settings在另一个 cpp 文件中使用此类时,程序无法编译:

// test.cpp
#include "Settings.h"

错误:C2079:“虚拟”使用未定义的类“用户”

可能的解决方案(我不喜欢)

  1. 一个解决方案是包含User.h在每个包含 的 cpp 文件中Settings.h,即:

    // test.cpp
    #include "User.h"
    #include "Settings.h"
    

    我不喜欢这种解决方案,因为我必须记住User.h每次包含Settings.h.

  2. 另一种解决方案是使用不推荐的DBO_EXTERN_TEMPLATES宏,即

    // Settings.h
    ...
    class Settings
    {
    public:
       ....
    };
    
    DBO_EXTERN_TEMPLATES(Settings)
    

    我不喜欢这个解决方案,因为这个宏不推荐,也没有记录。DBO_EXTERN_TEMPLATES不适用于所有编译器。

问题

一种。克服Wt::Dbo对象之间的循环依赖关系避免上述undefined class错误的最佳/首选方法是什么?

湾。为什么解决方案 1. 有效?

我创建了一个新的(一般 - 非Wt::Dbo特定)问题(使用 MCVE),以澄清具体情况:模板类的成员函数何时实例化?

参考

4

2 回答 2

3

我不熟悉Wt::Dbo,但我不相信这个问题是特定的。它更像是一个一般的 C++ 类设计问题,您需要解决/处理它;它实际上在 C++ 项目中相当普遍。

对于“最佳/首选方法”,这确实是一个见仁见智的问题。在您的情况下,如果您仍然有前向声明,您实际上可以同时拥有两者User.h并相互包含。Settings.h

例如,在Settings.h

// include guard
class User;
#include "User.h"

class Settings { ... };

然后在 中User.h,您可以执行以下操作:

// include guard
class Settings;
#include "Settings.h"

class User { ... };

我知道这看起来很奇怪,但这是一种确保您不必一直包含两个标题的方法。或者,您只需在一个标题中执行此操作,并确保您始终包含该标题。

一般来说,我首选的方法是,在头文件中,只包含头文件中绝对需要的内容,然后转发声明其余部分。然后在源文件中包含实际需要的标头。这样做的原因是,如果我需要更改一个头文件,我不必重新编译包含该头文件的所有源文件;它提高了编译过程的性能。

至于您关于解决方案 1 为何有效的问题,这是因为您如何包含文件。在该特定示例中,您甚至不需要包含Settings.h在源文件中,因为User.h已经这样做了。但是,让我们看看预处理器完成后它的外观。

当你包含User.h时,它首先包含Settings.h。包含基本上将内容复制到发生包含的当前文件中。所以,实际上,你User.h会看起来像这样:

// User.h
#include <Wt/Dbo/Dbo.h> // contents from this would be here
#include <string> // contents from this would be here

// Settings.h
#include <Wt/Dbo/Dbo.h> // contents NOT included, due to previous include and include guards
#include <string> // same as above

class User; // Forward declaration of User Wt::Dbo object

class Settings 
{
public:
  Wt::Dbo::ptr<User> user;

  template<class Action>
  void persist(Action& a)
  {
    Wt::Dbo::belongsTo(a, user);
  }
};

class User
{
public:
  Wt::Dbo::weak_ptr<Settings> settings;

  template<class Action>
  void persist(Action& a)
  {
    Wt::Dbo::hasOne(a, settings);
  }
};

What you can see now, is that when the Settings class is being defined, the User is already forward declared and can be used by the Settings class. When User is then defined, it has the full definition of Settings to work with. In your test.cpp file now, both Settings and User are fully defined, and hence can be used.

I hope this helps :)

于 2019-12-20T22:59:44.213 回答
0

Based on the answer of ChrisMM, another solution is to forward declare your class at the top of its header file:

Settings.h:

// include guard
class Settings;
#include "User.h"

class Settings { ... };

Users.h:

// include guard
class User;
#include "Settings.h"

class User { ... };

The advantage of this approach is that you only have to forward declare the class in its own header file and are allowed to just include the file in any other (header) file that need it.

于 2019-12-21T10:23:57.253 回答