成员类与#includes

时间:2011-01-30 01:07:03

标签: c++ class header-files

我最近发现在您的头文件中包含#include是不好的形式,因为使用您的代码的任何人都会获得他们不一定想要的所有额外内容。

但是,对于将成员变量定义为另一个类的类型的类,还有什么替代方法?

例如,我在以下方式做事的时间最长:

/* Header file for class myGrades */
#include <vector>           //bad
#include "classResult.h"    //bad

class myGrades
{
    vector<classResult> grades;
    int average;
    int bestScore;
}

(请原谅这是一个非常人为的例子)

所以,如果我想摆脱#include行,有什么方法可以保留vector或者我是否必须以完全不同的方式编写代码?

7 个答案:

答案 0 :(得分:3)

如果您的类直接包含给定类型的数据成员(顺便说一下,不是指向该类型的指针或对该类型的引用),那么您必须具有可用的类声明,以便编译器知道如何许多字节,一个对象的实例占用。这通常意味着您必须#include头文件。

但是,有一些称为编译器防火墙的技术可以让您以一种方式构造类,使类可以访问相应类型的对象而不直接包含它们。其中一个是pImpl idiom,其中您的类实现如下所示:

class MyClass {
public:
    /* ... */

private:
    struct Impl;
    Impl* pImpl;
};

在这里,您向前声明一个包含类的实现的结构Impl,然后在类中存储指向Impl结构的指针。由于在类中有一个指针并不需要知道被指向的对象的大小,所以这非常好。然后,在.cpp文件中,您可以定义Impl结构:

struct MyClass::Impl {
    /* ... */
};

您只需按照实际字段的pImpl指针即可实现所有类的成员函数。这样做的缺点是所有字段访问都需要指针间接,但是如此快速地更改类实现的能力确实有用。

沿着同一行的另一个选择是使类成为一个抽象基类,然后提供一个静态因子函数,该函数返回一个对象,该对象实际上包含实现的基类。例如:

class MyClass {
public:
    virtual ~MyClass(); /* Need virtual dtors in polymorphic classes! */

    virtual void doSomething() = 0;
    /* ... etc. ... */

    static MyClass* New(/* ... args ... */);
};

然后,在.cpp文件中,您可以定义一个实际完成所有工作的MyClass的具体子类:

class ActualClass: public MyClass {
public:
     void doSomething();
     /* ... etc. ... */

private:
     /* ... data members ... */
}

最后,实现New以创建实现类的新实例:

MyClass* MyClass::New(/* ... args ... */) {
    return new ActualClass(/* ... args ... */);
}

希望这有帮助!

答案 1 :(得分:2)

在标题中包含其他标题绝对没有错。

你需要确保你的标题包含它所依赖的所有标题,你应该避免包含无关的,不需要的标题。

答案 2 :(得分:2)

  

#includes in是不好的形式   你的头文件

我认为你不能用那种确定性说出来。如果你在一个带有std :: vector的头文件中有一个函数,你真的无法避免包含那个头文件。

你的头文件中不应包含UNNECESSARY包,这是对的。如果您的实现需要一些不需要在头文件中公开的内容,请将其保留在您的实现中。

但我不会忘记不在你的标题中包含内容。

答案 3 :(得分:1)

我最近发现在你的头文件中包含#includes是一种不好的形式,因为任何使用你代码的人都会得到他们不一定想要的额外包含。

......正如你现在发现的那样,这并不像听起来那么棒。减少你的依赖关系总是好的,如果你可以使用一个名字而不必知道它的样子,那么前向声明会更好......但是谁告诉你在你的标题中包含的不好的形式只是普通的FOS。

那些使用你的代码WANT的人完全不同意。如果他们要使用你的类,那么他们需要你的类直接依赖的对象的所有定义(而不仅仅是名称依赖)。如果您使用WTF类型的成员变量,那么某人只需要包含WTF的标题以便使用您的类,并且必须在您的类定义之前包含它。自己包含这样的标题是很多,很多,甚至更好,这样你的类的客户端就不必弄清楚fsck WTF的定义位置,并在包含你的类标题之前包含它。

换句话说,完全按照你被告知的对象。包含声明您的类所需的所有标题是一种很好的形式。

答案 4 :(得分:0)

编辑: 我不同意包括标题是不好的。如果需要,就需要它。 如果转发声明一个类,则不需要包含头文件,但是如果您的成员变量是指针或引用,则可以使用它。

我尽可能使用前瞻声明。 但是我不会更改类成员只是为了避免包含头文件,因为我没有看到任何错误。

答案 5 :(得分:0)

似乎不是每个人都同意减少头文件依赖性,但我肯定会这样做。随着项目规模的增加,不必要的包括大大增加了单个编译单元的构建时间,并且意味着在进行更改时需要重建更多的代码。

通常一个好的主意是让一个头文件只包含编译器知道对象大小的最小量,另一个包含任何内联函数定义。 sizeof(vector)通常是2 * sizeof(classResult *),它不依赖于classResult中的内容,所以在最初的例子中,你只需要转发声明classResult。

C ++有一个恼人的怪癖,其中一个结构必须被前向声明为一个结构,并且一个类必须被前向声明为一个类,尽管除了成员默认为公共或私有之外,这两个实际上是相同的。通常,尽可能地使用前向声明,因为它们可以大规模地减少头文件依赖性。

上面提到的pimpl习惯用法也很有用,但需要注意何时何地使用它,因为它可能需要额外的间接性来损害运行时性能。如果只在堆上创建对象,则可以将ctor设为私有,而是使用公共静态工厂方法。函数的实现可以将其转换为实数类型,例如

// In header file
class Foo
{
public:

    static Foo * Create();

    void DoSomething();

private:
    Foo();
    Foo( const Foo & );
    Foo & operator=( const Foo & );
};


// In c++ file
struct FooImplementation
{
    int bar;
};

Foo * Foo::Create()
{
    return (Foo*) new FooImplementation;
}

#include <stdio.h>

void Foo::DoSomething()
{
    FooImplementation * const self = (FooImplementation*) this;
    printf( "bar = %i", self->bar );
}

这种方法的主要问题是sizeof(Foo)没有给出正确的值(因此Create()函数),而且它是一个hack。就个人而言,我觉得减少编译时间的工作效率提高使这个黑客值得一试。

答案 6 :(得分:0)

哇,好吧,我完全错了。里维埃拉用他的评论来接近它。

我的消息来源是7:30左右 - http://channel9.msdn.com/shows/Going+Deep/C9-Lectures-Stephan-T-Lavavej-Standard-Template-Library-STL-3-of-n/

但是,回过头来看,他指的是“使用”指令。我一定是脑子里听过它。

有趣的答案,尤其是templatetypedef。谢谢大家(并为麻烦道歉)