前向声明?,包括守卫?,还是别的啥?

Posted

技术标签:

【中文标题】前向声明?,包括守卫?,还是别的啥?【英文标题】:Forward Declaration?, Include guard?, or something else?前向声明?,包括守卫?,还是别的什么? 【发布时间】:2013-09-12 08:15:38 【问题描述】:

我有一个名为 Token 的 父类

我有两个子类,ErrorToken 和 EndToken这些类中的每一个都需要能够创建另一个类的对象,并通过函数调用返回它。每个都有自己单独的头类。

所以,ErrorToken 需要能够创建新的 EndToken 对象并返回,而 EndToken 需要能够创建新的 ErrorToken 对象并返回。

在这方面取得成功的最佳方法是什么?如果它尽可能与交叉编译器兼容,我会更喜欢这样我不想使用一次编译指示。(但这基本上就是我要寻找的)。

理想情况下,我希望能够做这样的事情......

#ifndef ErrorToken
#include "ErrorToken.h"
#endif

但这似乎永远不会奏效(我猜那是错误的?有人可以帮我理解为什么吗?)。

我对前向声明的理解是它只适用于函数签名和指针(对吗?),所以我认为这不适用于我的情况,因为我需要它能够运行构造函数。 .或者编译器是否只需要知道构造函数在那一刻退出?

【问题讨论】:

在头文件中使用前向声明,并在实现文件中包含必要的头文件。 不支持#pragma once,您打算使用哪些奇怪的编译器?我认识的每个编译器都支持它。 你确定需要使用循环依赖吗? 【参考方案1】:

好吧,使用前向声明。正如你所说,那里有数百万种解释,现在有数百万零一个:

ErrorToken.h:

#ifndef H_ERROR_TOKEN
#define H_ERROR_TOKEN

#include "Token.h"

class EndToken;

class ErrorToken : public Token

public:
    EndToken makeEndToken();
;

#endif

EndToken.h:

#ifndef H_END_TOKEN
#define H_END_TOKEN

#include "Token.h"

class ErrorToken;

class EndToken : public Token

public:
    ErrorToken makeErrorToken();
;

#endif

在每个实现文件中,您现在可以愉快地包含两个标头:

#include "ErrorToken.h"
#include "EndToken.h"

ErrorToken EndToken::makeErrorToken()

    return ErrorToken();   // example


EndToken ErrorToken::makeEndToken()

    return EndToken();


正如@James Kanze 指出的那样,您可能对 C++ 的工作原理感到困惑。以下代码可能更符合您对 Java 的期望,并且在多态设计方式中更有意义:

class Token  virtual ~Token()   ;

class ErrorToken : public Token

    std::unique_ptr<Token> makeEndToken();
;

class EndToken : public Token

    std::unique_ptr<Token> makeErrorToken();
;

std::unique_ptr<Token> EndToken::makeErrorToken()

    return  new ErrorToken; 


std::unique_ptr<Token> ErrorToken::makeEndToken()

    return  new EndToken; 

由于您仅通过基指针处理对象,因此标头不需要了解有关其他派生类的任何信息。 (我留给您将代码细分为文件;每个块都进入一个单独的文件。)

【讨论】:

您可能会指出为什么您更改了他的包含警卫的名称。 (使用ErrorToken 作为定义类ErrorToken 的标头的包含保护不会很好地工作。) 另一点,与他的问题无关(但由于他来自 Java 背景,也许值得指出):如所写,您的函数按值返回。这对于继承层次结构中的对象来说有些例外(尽管它可能是合理的);大多数时候,您会返回一个指针 (ErrorToken*)。 @JamesKanze:我什至没有注意到 :-) @JamesKanze:整个问题充其量是微不足道的。 “通常”你会返回一个基指针,整个问题就会消失...... 感谢您的输入...返回指针与按值返回有什么好处?它只是防止程序不得不复制对象吗?【参考方案2】:

为什么是你的

#ifndef ErrorToken

错了?首先,将它放在头文件中,其次确保某些内容确实定义 ErrorToken


包含守卫和前向声明可能会让你摆脱这个困境:

//"ErrorToken.h"
#ifndef ERROR_TOKEN_INCLUDED
#define ERROR_TOKEN_INCLUDED

class EndToken;

class ErrorToken

public:
    EndToken DoSomething();
;
#endif

//"EndToken.h"
#ifndef END_TOKEN_INCLUDED
#define END_TOKEN_INCLUDED
class ErrorToken;

class EndToken

public:
    ErrorToken DoSomething();
;
#endif

之前关于循环依赖的讨论有几个:here、here 和here 然后,您可以在 cpp 文件中完成大部分工作,其中包含另一个标头,因此它知道完整的类定义,而不仅仅是前向声明,并且可以使用它。 不过先退后一步,问:“他们真的需要了解彼此吗?”

【讨论】:

我强烈建议为 include 守卫使用更详细的命名约定;说类似 ERRORTOKEN_H 的话。实际上,您的代码不会编译,因为您已将 #defined 类的名称设置为空字符串。 哦,我的话...我在想什么?

以上是关于前向声明?,包括守卫?,还是别的啥?的主要内容,如果未能解决你的问题,请参考以下文章

UITextView 还是别的啥?需要帮助理解

LINQ 还是别的啥?

放松 Segue 还是别的啥?

PhoneGap——还是别的啥?对于使用 Swipe 和 Pinches 的 iOS 应用程序

gcc mismatched-tags 选项给出“无法识别的命令行选项”

交叉引用、前向声明等:按啥顺序?