如何使用extern而不是#include在源文件中使用头文件中的类?

时间:2009-06-30 18:27:00

标签: c++ extern

如果我在outside.h中有一个班级,那么:

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

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


编辑:

#include是不允许的。

10 个答案:

答案 0 :(得分:6)

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

去寻找并包含文件!

答案 1 :(得分:5)

只是澄清一下。 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将找到该函数,并告诉它取消引用传递给Outside*的{​​{1}}。我认为这被称为“走很长的路”。

但事实就是如此。

如果你没有使用GetOutsideCount关键字,那么你可以通过以相同的方式添加以下两个函数来完全“让我们使用C ++就像它的C”模式(即通过前向声明和在extern旁边执行:

int GetOUtsideCount()

我更愿意吞下那个。这很像APR使用的策略。

答案 2 :(得分:4)

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

答案 3 :(得分:3)

你不能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();
}

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

答案 4 :(得分:3)

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

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

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

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

答案 5 :(得分:2)

如果您有一个愚蠢的要求,#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;
}

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

答案 6 :(得分:2)

我只能想到一个用例,你可以'extern'一个类,没有#include标题或复制类定义,正如其他人所建议的那样。

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

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

这仅适用于您从未在文件中拨打pOutside->GetCount()new Outside的情况。

答案 7 :(得分:2)

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

答案 8 :(得分:1)

我认为你误解了存储类,其中一个是外部的。

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

因此标记extern是变量而不是类定义/声明

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

答案 9 :(得分:1)

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

如果你真的不能使用#include,你会留下一个手动副本,如上所述,当有人改变原版时,这个问题显然已经过时了。这将彻底破坏您的测试代码,难以跟踪崩溃。

如果您坚持沿着本手册的路径走下去,您需要整个班级声明。从技术上讲,你可以省略某些零碎,但这是一个坏主意 - 显然你的团队对C ++结构没有深刻的理解,所以你可能会犯错。其次,C ++对象模型在编译器之间没有标准化。从理论上讲,即使是简单的非虚拟方法也可能会改变模型,从而破坏您的测试。

“长路径”实际上并不是不直接包含文件的重要原因。但是如果你真的不能,那么将整个头文件复制到#include所在的位置 - 这正是C预处理器无论如何都会做的。

相关问题