2010-07-07 85 views
2

我有一个很大的C++类,其中包括5个其他类,其中一些内部有其他类。标题(.h)文件的总长度非常大并且不可读。这就是我想要做的事:如何在C++中设计一个大类头文件?

// Foo.h file 
#ifndef __INCLUDE_FOO_H 
#define __INCLUDE_FOO_H 
namespace foo { 
class Foo { 
public: 
    #include "foo/Bar.h" 
    void f() { /* something here */ } 
}; 
} 
#endif 

这是子类:

// Foo/Bar.h file 
class Bar { 
} 

你觉得呢?这是一种合适的方法,还是我正在重新发明轮子?

+4

请注意,创建像__INCLUDE_FOO_H这样的名称在您自己的代码中是非法的。并且通常不需要复杂的类嵌套。 – 2010-07-07 07:36:58

+4

“我有一个很大的C++类,其中包含5个其他类,其中一些类还包含其他类。” ... *为什么?* ... – 2010-07-07 07:52:03

+0

我需要内部类,因为它们仅与父类相关,并且没有其他含义。例如,它是一个容器和其中的物品。 – yegor256 2010-07-07 08:02:45

回答

2

代替把内部类内部类的,你最好还是留给他们自己,最终在自己的命名空间:

// in file foo/bar.h 
namespace foo_detail 
{ 
    class Bar 
    { 
    }; 
}; 

// in file foo.h 
#include "foo/bar.h" 

class Foo 
{ 
private: 
    foo_detail::Bar theBar; 
}; 

这样你Foo类定义不是太复杂,你的定义文件很小(并且很容易浏览),所以在Foo类定义中不包含任何东西(尊重最小惊喜的原则 - 或者减少WTF/LOC的数量),并且当您查看Bar的定义,您可以清楚地看到它是foo的实现细节(如果没有其他名称,则通过命名空间名称)。

命名空间很好。相信命名空间。

2

我不会去为类声明中#include指令。我倾向于将它们在文件开头的一个地方分组。此外,由于您使用的是名称空间,因此文件Bar.h将具有不同的含义,它将被包含在代码中的其他位置。

关于在类的方法,我知道这可能是偶尔有用类的类,但你不能只是简单地创建单独的类,然后使用这些类的实例/引用/指针需要的地方?

和我的最后一点是,如果你已经有了巨大的头,你总是可以定义体的方法在不同的文件(即foo.h中只包含声明,所有的代码去foo.c的)。它使你的软件编译更复杂一些,但是使代码更具可读性。

+0

“你总是可以在不同的文件中定义身体方法” - 你_definitely_ *应该*。 – 2010-07-07 07:54:42

2

我会做的是真正考虑你是否需要所有这些嵌套类,或能将它们分成不同的类中的第一件事情?

如果你真的需要他们,那么你有什么工作,但在所有诚实,我只想把它们放在一个大文件。如果你像这样分割文件,你会混淆人们,他们会认为这些内部类在他们看到他们的头文件时处于命名空间范围。

+3

+1 *为最不惊讶的原则*:不要做花哨的事情,这将是读者意想不到的。 – 2010-07-07 07:40:39

2

你可以使用PIMPL,并将所有私人领域的私人相关类,如:

// foo.h 

class FooPrivate; /* Forward declaration */ 

class Foo { 
    public: /* Public fields, if any */ 

    Foo(); /* c-tor */ 
    ~Foo(); /* d-tor */ 

    /* Public methods */ 
    int how_old_is_frisky(); /* Sample method declaration */ 

    private: /* No private fields */ 
     FooPrivate *priv; 
} 

// foo.cpp 

// may be a struct, it's plain ol' data container 
class FooPrivate { 
    public: 
     /* Any data private to Foo. As FooPrivate 
      class is declared in .cpp file, it 
      will be accessible only from this file */ 
     Bar bar; 
     Cat frisky; 
     Dog goggie;   
} 

/* Constructor - remember to allocate private data */ 
Foo::Foo() 
{ 
    priv = new FooPrivate; 
} 

/* Destructor, remember to deallocate private data */ 
Foo::~Foo() 
{ 
    delete priv; 
} 

/* Sample method definition */ 
int Foo::how_old_is_frisky() 
{ 
    return priv->frisky.get_age(); 
} 

这样:

  1. 你的头是短了很多
  2. 您的私人数据是真正的私有
  3. 为2的结果,如果你改变你的类的实施,你不必重新编译仅使用接口的每个源文件。

此外,如示例所示,在源文件(.cpp)文件中放置方法定义(即方法体)。这样他们将只被编译一次。如果将它们放在标题中,则每次编译标题时都会对它们进行编译。

--- --- EDIT

的2点和3不是隐藏从恶意观看者的眼睛的数据成员。在C++中有一个“特性”,它使私有成员成为接口的一部分。理想情况下,私有成员应该是实现的一部分,因此接口的用户不应该受实现变化的影响。不幸的是,C++并非如此。当您更改私人成员(即添加新成员)时,您将更改整个类接口,因此您的界面的用户(即包含头文件的所有文件)必须重新编译。

但是,当您使用pointer-to-implementation模式,唯一的私有成员是一个指针,所以只能做公共成员变动引发的客户端代码重新编译(除非你,当然,改变priv指针)。要查看真实生活中的示例,请查看广泛使用PIMPL模式的Qt库。

+0

我认为这会使代码变得更加复杂('* priv-> bar'而不是'bar'),因此获得的收益非常小。隐藏私人会员的意义何在?如果有人试图破解你的课堂实施,并做了无证的事情,无论如何,这是他自己的问题。 – ereOn 2010-07-07 08:00:10

+0

但是我可以在全局范围内轻松完成'FooPrivate f = new FooPrivate'。这个构造将没有意义,但将是C++有效的 – yegor256 2010-07-07 08:04:03

+1

@Vincenzo你可以简单地在Foo的私有部分中声明该类,并在你的cpp中声明它为Foo :: FooPrivate类来避免这个问题 – Pieter 2010-07-07 08:23:34