简体   繁体   中英

How to design a big class header file in c++?

I have a big C++ class, which includes 5 other classes inside, and some of them have other classes inside. The total length of a header ( .h ) file is very big and it is unreadable. This is what I'm trying to do:

// 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

This is the sub-class:

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

What do you think? Is it a proper approach or I'm re-inventing a wheel?

I would not go for the #include directive inside the class declaration. I tend to group them in one place at the beginning of the file. Furthermore, since you're using namespace, the file Bar.h would have a different meaning would it be included somewhere else in your code.

Concerning the class in class in class approach I know that this can be useful on occasion, but can't you simply just create separate class and then use instances/references/pointers of these classes wherever needed?

and my last point is, if you've got huge header, you can always define body methods in separate files (ie Foo.h only contains declarations, all the code goes to Foo.c). It make the compiling of your software a little bit more complex, but makes the code more readable.

The first thing I would do is really consider whether or not you need all those nested classes, or could they be broken up into separate classes?

If you really do need them then what you have will work, but in all honesty, I would just put them in one big file. If you split up files like that, you're going to confuse people, who will think that those inner classes are at namespace scope when they see their header files.

You could use PIMPL , and move all private fields to a private related class, eg.:

// 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 ();
}

This way:

  1. Your header is a lot shorter
  2. Your private data is really private
  3. As a consequence of 2, if you change implementation of your class, you do not have to recompile every source file that uses merely interface of it.

Moreover, as shown in example, put method definitions (ie method bodies) in source ( .cpp ) files. This way they will be compiled only once. If you put them in header, they will be compiled every time header is compiled.

--- EDIT ---

The point of 2 and 3 is not to hide data members from malicious viewers' eyes. There is a "feature" in C++ that makes private members part of class interface . Ideally, private members should be part of implementation so users of class interface should not be affected in changes in implementation . Unfortunately, it's not the case in C++. When you change private members (ie. add new member), you change whole class interface , so users of your interface (ie all files that include your header file) have to be recompiled.

However, when you use pointer-to-implementation pattern, the only private member is one pointer, so only changes done to public members trigger recompilation of client code (unless you, of course, change priv pointer). To see real life examples, take look at Qt library, which uses PIMPL pattern extensively.

Instead of making the inner classes inner classes, you're better off leaving them on their own, eventually in their own namespace:

// 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;
};

This way your Foo class definition is not overly complicated, your definition files are small (and easy to skim over), you do not include things in your Foo class definition (respecting the principle of least surprise - or decreasing the number of WTF/LOC as they say) and when you look at the definition for Bar, you can clearly see it's an implementation details for foo (by the namespace name if nothing else).

Namespaces are good. Trust the namespaces.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM