【问题标题】:How can I use a class from a header file in a source file using extern but not #include?如何使用extern而不是#include在源文件中使用头文件中的类?
【发布时间】:2023-08-01 00:16:01
【问题描述】:

如果我在outside.h 有课,比如:

class Outside
{
   public:
       Outside(int count);
       GetCount();
}

如何使用extern 关键字在framework.cpp 中使用它,我需要在其中实例化类并调用GetCount


编辑:

#include 是不允许的。

【问题讨论】:

  • 为什么需要使用extern?你知道 extern 是做什么的吗?
  • 我猜您并不真正了解您的要求。也许你不能包含第三方库,但没有人会禁止使用“#include”指令。
  • 如果不能使用include,为什么还要有一个.h文件? .h 文件通常不编译为单独的翻译单元。
  • 荒谬的问题。我认为你需要问一个更基本的问题来帮助你更好地理解事情。您正在混合不相关的想法(外部文件与包含文件)。即使您使用外部库,您也可以通过包含的头文件通过其定义的函数来使用它。
  • 请把项目要求报给我们。这是嵌入式代码吗?它是否链接到 C 代码?您是否因代码更改而在管理雷达下飞行?您是否在供应商控制的环境中进行编译,对构建的控制有限?这个要求背后的原因是什么?

标签: c++ extern


【解决方案1】:

这里的每个人都有点太温柔了。 绝对没有理由不想包含 .h 文件。

去尝试并包含文件!

【讨论】:

    【解决方案2】:

    只是为了澄清。不可能extern类:

    class Outside
    {
        public:
            Outside(int count);
            GetCount();
    }
    

    但是,一旦您在 framework.cpp 中获得了可用的类,您就 CAN extern 一个 Outside 类型的对象。您需要一个 .cpp 文件来声明该变量:

    #include "outside.h"
    
    Outside outside(5);
    

    然后您可以通过extern 在另一个文件中引用该对象(只要您在编译项目时链接到正确的对象文件):

    #include "outside.h"
    
    extern Outside outside;
    int current_count = outside.GetCount();
    

    extern 用于表示“我知道此程序运行时将存在具有此名称的这种类型的变量,我想使用它。”它适用于变量/对象,而不是类、结构、联合、typedef 等。它与static 对象没有太大区别。

    您可能正在考虑前向声明类以减少编译时间,但对此有一些限制(您只能将对象用作不透明指针,并且不能在它们上调用方法)。

    您也可能意味着对用户隐藏Outside 的实现。为此,您需要阅读 PIMPL 模式。


    更新

    一种可能是向 Outside.h 添加一个免费函数(我还添加了一个命名空间):

    namespace X {
        class Outside {
            int count_;
            public:
                Outside(int count) : count_(count) { }
                int GetCount()
                {
                    return count_;
                }
        };
    
        int GetOutsideCount(Outside* o);
    }
    

    在 .cpp 文件中实现该功能。当你这样做的时候,你不妨把你打算的全局变量 extern (注意,变量本身不需要是指针):

    #include "outside.h"
    
    namespace X {
        int GetOutsideCount(Outside* o)
        {
            return o->GetCount();
        }
    }
    
    X::Outside outside(5);
    

    然后在您的程序中执行此操作(请注意,您不能在 outside 上调用任何方法,因为您没有包含 outside.h 并且您不想通过添加类的新定义来违反单一定义规则或那些方法;但由于定义不可用,您需要将指针传递给 outside 而不是 outside 本身):

    namespace X {
        class Outside;
        int GetOutsideCount(Outside* o);
    }
    
    extern X::Outside outside;
    
    int main()
    {
        int current_count = GetOutsideCount(&outside);
    }
    

    委婉地说,我认为这是可憎的。您的程序将找到GetOutsideCount 函数,通过传递Outside* 来调用它。 Outside::GetCount 实际上被编译为一个普通函数,它接受一个秘密 Outside 对象(在 Outside::GetCount 内,该对象通过 this 指针引用),所以 GetOutsideCount 会找到该函数,并告诉它取消引用传递给GetOutsideCountOutside*。我认为这就是所谓的“走很长的路”。

    但事实就是如此。

    如果您不习惯使用 extern 关键字,则可以通过以相同方式添加以下两个函数(即通过前向声明和实现权)来完全“让我们像 C 一样使用 C++”模式在int GetOUtsideCount()旁边:

    Outside* CreateOutsidePointer(int count)
    {
        return new Outside(count);
    }
    
    void DestroyOutsidePointer(Outside* o)
    {
        return delete o;
    }
    

    我更愿意接受它。这很像APR使用的策略。

    【讨论】:

      【解决方案3】:

      你不做外部课程。只需包含“outside.h”并创建Outside 的实例。

      【讨论】:

      • 是的,但在这种情况下,不允许包含。项目要求。
      • 文件中没有包含声明的类是不可能使用的。否则,编译器不知道类的结构是什么,也不知道如何使用它。能否介绍一下该项目的一些细节?
      • 是的,写声明很好。我怎么做?我只是不知道在 framework.cpp 中“包含”什么。
      • @Joan Venge,在现实生活中,项目要求通常有些有意义。它们不是诸如“不包含”、“暂时不”或“只用你的左脚编写代码”之类的任意东西。
      【解决方案4】:

      你不能 extern 类,但你可以 extern 一个创建实例的函数。在消费者代码中:

      class Outside;
      
      extern Outside* MakeAnOutside(int count);
      extern int GetOutsideCount(Outside* outside);
      

      然后在outside.h中:

      Outside* MakeAnOutside(int count)
      {
         return new Outside(count);
      }
      
      int  GetOutsideCount(Outside* outside)
      {
        return outside->GetCount();
      }
      

      但是..这可能不是一个好主意..

      【讨论】:

      • 谢谢,所以我不能创建外部构造函数?
      • no.. 编译器需要头文件直接调用构造函数。
      • 您不需要extern 做任何事情。 extern 用于任何类声明之外的全局变量。顺便说一句,它也不适用于函数。
      • @John Kugelman:extern 适用于函数声明。无论如何,函数默认都有外部链接,所以它通常是多余的。
      【解决方案5】:

      包含文件用于定义,包括类定义。 extern 用于变量。

      如果您在源文件中没有类的定义,那么您唯一能做的就是用class Outside; 声明它并通过指针传递实例。您实际上不能对实例做任何事情,包括构造、销毁或调用成员函数(如GetCount())。为此,您根本不需要extern;仅当您想引用另一个源文件中的变量时,您才不会对它做任何额外的事情。

      这里没有不使用#include 的正当理由。唯一的选择是将头文件复制并粘贴到源文件中,这要糟糕得多。任何告诉你不要使用#include 的人都不懂C++,显然任何认为extern 与这里有任何关联的人肯定不会。

      如果可能的话,您应该请一位经验丰富的 C++ 开发人员来帮助您学习、建立良好的编码风格并指导您如何开发 C++。我怀疑你在做其他事情,以后会变成坏主意。

      【讨论】:

        【解决方案6】:

        如果您有一个不允许 #include 的愚蠢要求,那么您必须将类声明复制并粘贴到您的 .cpp 文件中。需要我说这是一个非常糟糕的主意吗?

        这个要求的原因是什么?我很难建议你如何做到这一点。如果您试图避免源文件中出现较长的 #include 路径,这是构建问题而不是源代码问题。

        您应该使用 gcc -I 选项将目录添加到包含路径中,或者为您的编译器设置任何等效项。

        如果您真的非常确定对此,您会想要这样的:

        framework.cpp

        // FIXME: Should #include "outside.h" but not allowed.
        class Outside
        {
           public:
               Outside(int count);
               GetCount();
        
               // Omitted
               // void SomeUnusedMethod();
        };
        
        <code for framework.cpp here>
        
        void UseOutside()
        {
            Outside o(5);
            std::cout << o.GetCount() << std::endl;
        }
        

        然后,我强烈建议您保留声明原样,这样它就可以直接从头文件中复制和粘贴。但是如果你想修剪它,你可以省略任何你不使用的非虚拟方法。您需要保留所有变量。

        【讨论】:

        • 你的意思是整个cpp?如果我不需要调用 GetCount 怎么办?那我需要复制粘贴什么呢?
        • 对不起,我的意思是整个 .h 文件。
        • 基本上认为外面的类有100个函数,我只需要1个,我需要复制什么,请附上示例。不知道复制出来的代码是否也需要extern。
        • 你是对的。但要求是集成的“痛苦更少”。否则,必须使用非常长的路径包含该文件。这是给我的建议,所以我不知道。
        • @Joan Venge:如果您在同一个程序的多个翻译单元中使用同一个类,那么每个定义必须由完全相同的标记序列组成。这就是为什么几乎每个人在需要在翻译单元之间共享类定义时都#include 一个头文件。
        【解决方案7】:

        我只能想到一个用例,您可以“外部”一个类,而无需#include 标题或复制其他人建议的类定义。

        如果您需要保留指向类的指针,但您从不直接取消引用它,只传递它,那么您可以在文件中执行以下操作:

        class Outside;
        class MyClass
        {  
           Outside* pOutside;
           void SetOutsidePointer(Outside *p) {pOutside = p;}
           Outside* GetOutsidePointer() { return pOutside;}
           /* the rest of the stuff */
        }
        

        这只有在您从未在文件中调用 pOutside-&gt;GetCount()new Outside 时才有效。

        【讨论】:

          【解决方案8】:

          将您的 Outside 类的包含放在 StdAfx.h 或 framework.cpp 已经包含的任何其他头文件中。

          【讨论】:

            【解决方案9】:

            我认为您误解了存储类,其中之一是外部。

            对象和变量声明为 extern 将在另一个翻译单元或封闭范围中定义的对象声明为具有外部链接。”

            所以标记 extern 是针对变量而不是类定义/声明

            因此,如果您不能包含 .h,我建议您将 .h 和 .cpp 构建为静态库或 dll 并在您的代码中使用

            【讨论】:

              【解决方案10】:

              哎呀...我们将类放在头文件中并使用#include 将类(或其他)声明复制到多个 cpp 文件(称为编译单元)中。

              如果你真的不能使用#include,你会留下一个手动副本,如上面所建议的,当有人更改原始文件时,它有一个明显的过时问题。这将彻底破坏您的测试代码,导致难以跟踪的崩溃。

              如果您坚持按照本手册的方式进行复制,则确实需要整个类声明。从技术上讲,您可以省略某些零碎的部分,但这是一个坏主意——显然您的团队对 C++ 结构没有深入的了解,因此您可能会犯错误。其次,C++ 对象模型没有跨编译器标准化。从理论上讲,即使是简单的非虚拟方法也可能会改变模型,从而破坏您的测试。

              “长路径”确实不是不直接包含文件的好理由。但如果你真的做不到,请将整个头文件复制到 #include 本来应该存在的位置——这正是 C 预处理器要做的事情。

              【讨论】: