开发者

Turning one file with lots of classes to many files with one class per file

How do I turn one file with lots of classes to many files with one class per file? (C\C开发者_StackOverflow社区++)

So I have that file with such structure: Some includes and then lots of classes that sometimes call each other:

#include <wchar.h>
#include <stdlib.h>
//...
class PG_1 {
  //...
}
class PG_2 {
  //...
}
//......
class PG_N {
  //...
}


If you're not using revision control (tsk tsk):

  1. Back up your entire project in case you mess up.
  2. Cut and paste each class into its own classname.h and classname.cpp files. Replace classname with the name of the class. Update the include guards.
  3. Add the #include directives that you think are necessary for each class's dependencies.
  4. Delete multiclass.h and multiclass.cpp.
  5. Add the single-class files to your project or makefile. Remove the multi-class files from your project or makefile.
  6. Build the project or makefile.
  7. If it fails to build, fix the problem (e.g. a missing #include) and go to step 6.
  8. Once it builds, run your tests.
  9. If the tests fail, diagnose and fix the problem, and go to step 6.

If you are using a revision control system that supports file-level branching (such as Perforce, or maybe Subversion?), you should take care to preserve the revision history, so that other developers can find old changes:

  1. Do the rest of these steps in a development branch, not the trunk.
  2. For each class name in multiclass.h and multiclass.cpp, integrate multiclass.h and multiclass.cpp into a separate classname.h and classname.cpp for that class.
  3. Submit a changelist containing all of these integrations. This makes N copies of the original file, and they all have a revision history pointing to the original.
  4. Check each new file out for edit.
  5. Remove everything from each new file except the code that is needed for that particular class, and update the include guards.
  6. Add the #include directives that you think are necessary for each class's dependencies.
  7. Check the old multiclass.h and multiclass.cpp out for delete.
  8. Check out the project or makefile for edit.
  9. Add the single-class files to your project or makefile. Remove the multi-class files from your project or makefile.
  10. Build the project or makefile.
  11. If it fails to build, fix the problem (e.g. a missing #include) and go to step 10.
  12. Once it builds, run your tests.
  13. If the tests fail, diagnose and fix the problem, and go to step 10.
  14. Submit the changelist with all of the edits.

If you are using a revision control system that doesn't support file-level branching, then some combination of the two methods should work. Hopefully you get the idea.


How do I turn one file with lots of classes to many files with one class per file?

Edit: Something I should mention on refactoring in general. There's no such thing as a big step in refactoring (not unless you want to destroy your code-base). You should always make transactional transformations to the code, with validity checks in between (transactional means they are clearly delimited and can be committed and rolled-back at any point). If you have big steps that means you haven't broken them enough into small steps. Also, each step should do one thing and one thing only. (end edit).

Steps:

  1. back up everything (all affected files)

    This means perform a commit in your source-control system. If you don't use any source control, install mercurial before continuing (go ahead, we'll wait ;)) - you can thank me later :D

  2. create new files (.h and .cpp file) for the first class you want to remove, then include the new .h file in your old header (the one with many classes).

  3. move the class declaration in the .h file and the class implementation in the new .cpp file.

  4. remove the declaration / implementation from the old file. At this point compile the code, and copy #include dirrectives from the old files to the new files until everything compiles. At this point you should have your class moved to separate files, and included in the old header file. Do not copy all includes, only enough for your moved class to compile correctly.

  5. compile the code (eventually run the application and make sure everything runs).

  6. perform a commit in your source control system.

  7. remove the included new file from your old (multiple classes) header file and compile. go through the code and include the new file in whatever files you get errors.

  8. repeat the steps 5 and 6.

  9. pick new class, go back to step 1.

Possible mistakes and caveats:

  • you may be tempted to make other changes to the new code while moving it to new files. Do not make any changes. At the end of a cycle (after step 8) you should have the same code you had before step 1, just in different files. Any changes you make to the code in between the steps will be difficult to separate from the refactoring, difficult to track later and difficult to reverse. It's not worth the hassle, especially when you can make the same changes once the refactoring cycle is completed (with no other problems).

  • you may be tempted to work on multiple classes at the same time. Don't do that either. If you work in cycles you are always able to revert the code (in your SCM) to versions of the code that worked and you have modular changes. It is also very easy to track changes and check you didn't introduce new bugs if you go one class at a time.

Sidenote: If you use a branching SCM you will be able to work on the tasks in parallel, to interrupt your refactoring (commit everything - let's call this "branch head A"), go back to the code before you started refactoring, make some other changes, commit those (call them "branch head B"), then go back to branch head A, finish your refactoring, then merge A and B and you're in business.


There are several ways to accomplish that.

The most straight forward would be to read the file one line at the time and detect if that line starts a class.

Then start detecting matching braces... you know if you find { +1 and } -1 until you reach zero. Yes, there's more to that, but that's the main part.

Select that block and write it on another file.

On the other hand, IF you're using Visual Studio, would be to create a macro and use the DTE to peruse the FileCodeModel of the currently open file, and create a file for each top level class in that.


Two basic coding style patterns are possible:

  1. Class declarations in headers, member function/static data instantiation in .cpp files.
  2. Class definitions with in-line implementation in headers

Option 2 may lead to code bloat since if you use the class in multiple compilation units you may get multiple copies of the implementation in the final link; it is down to the linker to eradicate this and it may or may not do so - YMMV. It also allows users of the class access to the implementation, which may be undesirable in some cases.

A combination of the two with simple getter/setter functions in-lined and larger code bodies in separate compilation units is an option. Often if I have a constructor that is entirely implemented by the initialiser list, I will include its empty body in-line.

Example for class cExampleClass:

cExampleClass.h

class cExampleClass
{
public:
    cExampleClass() ;
    ~cExampleClass() ;
    void memberfn() ;
} ;

cExampleClass.cpp

cExampleClass::cExampleClass() 
{
    // body
}

cExampleClass::~cExampleClass()
{
    // body
}

void cExampleClass::memberfn()
{
    // body
}

or in-lined:

cExampleClass.h

class cExampleClass
{
public:
    cExampleClass()
    {
        // body
    }

    ~cExampleClass()
    {
        // body
    }

    void memberfn()
    {
        // body
    }
} ;

In both cases any source file that then uses cExampleClass simply includes the cExampleClass.h, and cExampleClass.cpp (if it exists) is separately compiled and linked.

Note that if the class includes static data member variables, these must be instantiated in a separate compilation unit in order for there to be just one common instance. Example:

cExampleClass.h

class cExampleClass
{
public :
    static int staticmember ;
} ;

cExampleClass.cpp

int cExampleClass::staticmember = 0xffff ;


Do you know how to do it manually? If you know, writing a program to do the same thing is straightforward.


There might not be an easy way to do this. The problem is you have to get the #includes right, split the code correctly to different header and cpp files, and if your classes have cyclic dependencies among themselves, you have to deal with them correctly, or better, try to resolve those dependencies to make them non-cyclic.

Best suggestion I can give you: first try to do this manually for two or three classes. Then decide what kind of physical class layout you need. Afterwards, try to write a program. Don't try to write a program unless you fully understand what to do.

By the way, how many classes/files do have?

EDIT: To get a better notion of what a good physical class-to-file layout may be, I suggest to read Large Scale C++ Design from John Lakos. Is a little bit outdated, since it contains nothing about precompiled headers, but still useful.


I suggest writing a script (in your favorite scripting language) to extract the class declarations into their own file. If you're not good at scripting languages, you can always write a C++ program to extract the class declarations.

Another possibility may be to use an IDE with refactoring capabilities or a refactoring application. I have never used these, so I can't advise on their capabilities.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜