An easy way to reduce the compile time of C++ code

  • 2020-05-10 18:35:45
  • OfStack

c + + code include the header file and the implementation of two parts, 1 a header file is available to others (also called the client), but as the header file changes, no matter how small the change, have to recompile all references to his file, compile will spend time, if you do have a larger project (such as 2 encapsulation chrome such development), compile 1 time again will waste most of the time to work, so do the 1 day is very tired, but your boss says you have no output, the results you are fired, whether very hatred ah, if you see this passage earlier, You will be more efficient than your colleagues so 1, then by fired will not be you, you say this article is not worth a thousand gold! Just kidding :)

Anyway, there are only three ways I know how to introduce compile time:

1. Remove unnecessary #include and use forward declarations instead (forward declared)

2. Remove unnecessary 1 large number of private member variables and use the "impl" method instead

3. Remove unnecessary inheritance between classes

In order to make clear the three points, it is better to give an example, which I will improve step by step (because I also found out at 1:1:00, if there is something wrong, you can rest assured of the spray, I will argue with you in the end, hehe).

For now, suppose you find a new job and take over a class written by a former programmer, as follows


// old.h:  This is the class you receive 
   //
   #include <iostream>
   #include <ostream>
   #include <list>
 
   // 5  a   , respectively, file , db, cx, deduce or error  .   Limited level no template class 
   //  only  file and cx  A virtual function .
   #include "file.h" // class file
   #include "db.h" // class db
   #include "cx.h" // class cx
   #include "deduce.h" // class deduce
   #include "error.h" // class error
 
   class old : public file, private db {
   public:
     old( const cx& );
    db get_db( int, char* );
    cx get_cx( int, cx );
    cx& fun1( db );
    error fun2( error );
    virtual std::ostream& print( std::ostream& ) const;
   private:
    std::list<cx> cx_list_;
    deduce    deduce_d_;
   };
    inline std::ostream& operator<<( std::ostream& os,const old& old_val )
    { return old_val.print(os); }

If you have already seen what the problem is, don't bother to read the next one. You are a master. These basic knowledge are too childish for you

First, how to use # 1: remove unnecessary #include

This class refers to 5 header files, which means that all the header files referenced by those 5 header files are also referenced. In fact, you don't need to refer to 5, just 2

1. Remove unnecessary #include and use the forward declaration instead (forward declared)

When I first learned c++, I copied from c++ primer, and when I saw the input and output, I added iostream header. After a few years, I now know that this is not the case. I just defined the output function here, and just referred to ostream

1.2.ostream header should not be replaced by iosfwd. Why? The reason is that parameters and return types can be compiled as long as they are declared forward


typedef basic_ostream<char, char_traits<char> > ostream;

inline std::ostream& operator<<( std::ostream& os,const old& old_val )

{ return old_val.print(os); }

In addition, if you say that this function operates on ostream objects, you still need #include < ostream > You're only half right. Yes, this function operates on ostream objects, but look at its implementation.

There is no definition of a statement like std::ostream os. However, whenever such a definition occurs, the corresponding header file of #include must be used, because this is a request to the compiler to allocate space, and if only class XXX is declared forward; How does the compiler know how much space to allocate to this object!

Here, the old.h header file can be updated as follows:


// old.h:  This is the class you receive 
   //
   #include <iosfwd> // New replaced header file 
   #include <list>
 
   // 5  a   , respectively, file , db, cx, deduce or error  .   Limited level no template class 
   //  only  file and cx  A virtual function .
   #include "file.h" // class file  .   As a base class cannot be deleted, the compiler does not know about the instantiation if it is deleted old  Object how much space is allocated 
   #include "db.h" // class db .   Cannot be deleted as a base class, ibid 
   #include "cx.h" // class cx
   #include "deduce.h" // class deduce
   // error  Used only as a parameter and return value type,   Replace with the forward declaration #include "error.h" 
   class error ; 
 
   class old : public file, private db {
   public:
     old( const cx& );
    db get_db( int, char* );
    cx get_cx( int, cx );
    cx& fun1( db );
    error fun2( error );
    virtual std::ostream& print( std::ostream& ) const;
   private:
    std::list<cx> cx_list_; // cx  Is a template type, neither a function parameter type nor a function return value type, so cx.h  The header file cannot be deleted 
    deduce    deduce_d_; // deduce  Is a type definition and does not delete its header file 
   };
    inline std::ostream& operator<<( std::ostream& os,const old& old_val )
    { return old_val.print(os); }

So far, deleted 1 some code, is not in a good mood, it is said to see how high the level of a programmer, not to see how much code he wrote, but to see how much code he wrote less.

If you're interested in C++ programming for one more step, you'll still read the next text, one more step to remove the code, but this time it's a different approach

2. Remove unnecessary 1 large number of private member variables and use the "impl" method instead

2.1. Use "impl" to write code to reduce the compilation dependence of client code

The impl method simply puts all the private member variables of the class into one impl class, and then keeps only one impl* pointer to the private member variables of the class. The code is as follows


// file old.h
   class old {
    // Public and protected members 
    // public and protected members
   private:
   // Private member,   As long as any 1 The number of header files changes or the number of members increases, decreases, all references old.h The client must be recompiled 
    // private members; whenever these change,
    // all client code must be recompiled
   };

Rewrite it like this:


// file old.h
   class old {
   // Public and protected members 
    // public and protected members
   private:
    class oldImpl* pimpl_;
    //  Replace all private member variables with this impl Pointer, the pointer only needs a forward declaration to compile, which puts the forward declaration and definition pointer in place 1 Since,   Absolutely. 
    // Of course, you could write it separately 
     // a pointer to a forward-declared class
   };
 
   // file old.cpp
   struct oldImpl {
   // The real member variables are hidden here,   Change at will,   The client code does not need to be recompiled 
    // private members; fully hidden, can be
    // changed at will without recompiling clients
   };

I don't know if you understand, please write a random class experiment, which is exactly what I did. Of course, everything has its advantages and disadvantages, and the following is a simple comparison:

使用impl 实现类

不使用impl实现类

优点

类型定义与客户端隔离, 减少#include 的次数,提高编译速度,库端的类随意修改,客户端不需要重新编译

直接,简单明了,不需要考虑堆分配,释放,内存泄漏问题

缺点

对于impl的指针必须使用堆分配,堆释放,时间长了会产生内存碎片,最终影响程序运行速度, 每次调用1个成员函数都要经过impl->xxx()的1次转发

库端任意头文件发生变化,客户端都必须重新编译

The change to impl implementation looks like this:


//  only  file and cx  A virtual function .
   #include "file.h" 
   #include "db.h" 
   class cx;
   class error ; 
 
   class old : public file, private db {
   public:
     old( const cx& );
    db get_db( int, char* );
    cx get_cx( int, cx );
    cx& fun1( db );
    error fun2( error );
    virtual std::ostream& print( std::ostream& ) const;
   private:
class oldimpl* pimpl; // Forward declaration and definition here 
   };
    inline std::ostream& operator<<( std::ostream& os,const old& old_val )
    { return old_val.print(os); }
 
//implementation file old.cpp
class oldimpl{
std::list<cx> cx_list_;
deduce    dudece_d_;
};

3. Remove unnecessary inheritance between classes

class old inherits file and db classes. file is a public inheritance, db is a private inheritance, file is understandable, because file has virtual functions, old needs to redefine it, but according to our hypothesis, Only file and cx have virtual functions. What about private inheritance db? ! So the only possible reason for 1 is:

By private inheritance - making a class unable to be used as a base class to derive other classes, similar to the Java keyword final, is clearly not intended, so this private inheritance is completely unnecessary. Instead, use the functionality provided by the db class in an inclusive manner

Delete the "db.h" header file, and put an instance of db into the impl class. The resulting class looks like this:


//  only  file and cx  A virtual function .
   #include "file.h" 
   class cx;
   class error;
   class db;
   class old : public file {
   public:
     old( const cx& );
    db get_db( int, char* );
    cx  get_cx( int, cx );
    cx& fun1( db );
    error fun2( error );
    virtual std::ostream& print( std::ostream& ) const;
   private:
    class oldimpl* pimpl; // Forward declaration and definition here 
   };
    inline std::ostream& operator<<( std::ostream& os,const old& old_val )
    { return old_val.print(os); }
 
//implementation file old.cpp
class oldimpl{
std::list<cx> cx_list_;
deduce    dudece_d_;
};

Summary 1:

This article is just a brief introduction to a few ways to reduce compilation time:

1. Remove unnecessary #include and use forward declarations instead (forward declared)

2. Remove unnecessary 1 large number of private member variables and use the "impl" method instead

3. Remove unnecessary inheritance between classes

I hope these are helpful to you. If I don't make it clear enough, please refer to the attachment for a complete example. You are also welcome to comment. Oh, in the next article I will make a detailed analysis of the implementation of impl, looking forward to it...


Related articles: