If you think that recompiling a file for a short time or a long time does not matter, anyway, you need to recompile, then you can choose to skip this article, but also recommend browsing.
If you want to learn or care about this piece of content, then this article is bound to bring you a harvest.
First I don't give the definition of dependency, I give an example.
Copy Code code as follows:
Class peopel{
Public
People (const std::string & name,const date& brithday,image Img)
std::string name () const;
Date birthdate () const;
Image img () const;
...
Private
Std::string thename; Name
Date thebirthdate; Birthday
Image img; Image
};
If the compiler does not know the definition of class string,date and image, class people cannot be compiled. Generally this definition is provided by the header file contained in the #include, so these preprocessing commands are generally people above
Copy Code code as follows:
#include <string>
#include "date.h"
#inblude "Image.h"
Class peopel{
Public
People (const std::string & name,const date& brithday,image Img)
std::string name () const;
Date birthdate () const;
Image img () const;
...
Private
Std::string thename; Name
Date thebirthdate; Birthday
Image img; Image
};
Then there is a compilation dependency between the people definition file and the three files. If any of these header files are changed, or if these headers depend on any other header file to change, then each file containing the people class needs to be recompiled, and the people class file needs to be recompiled. Think that if a project contains thousands of files, each file contains several other files, in turn, to change a file content, then you need to almost recompile the entire project, which can be said very bad.
We can make the following changes
Copy Code code as follows:
namespace STD {
class string;
}
class Date;
class Image;
class peopel{
public:
people (const std::string & Name,const D ate& brithday,image& Img)
std::string name () const;
Date birthdate () const;
Image img () const;
...
Private:
std::string thename; //Name
Date thebirthdate; //Birthday
Image img; //Picture
};
This will recompile only if the interface is people, but there is a problem, the 1th string is not class, it is a typedef basic_string<char> string. Therefore, the preceding statement is incorrect (attached to the STL complete code), and the correct predecessor declaration is more complex. In fact, for the standard library section, we can just include it through the #include preprocessing commands.
Copy Code code as follows:
#ifndef __string__
#define __string__
#include <std/bastring.h>
extern "C + +" {
typedef basic_string <char> String;
typedef basic_string <wchar_t> wstring;
}//extern "C + +"
#endif
Another problem with the predecessor declaration is that the compiler must know the size of the object during compilation to allocate space.
For example:
Copy Code code as follows:
int main (int argv,char * argc[])
{
int x;
People p (parameter);
...
}
When the compiler sees the definition of x, it knows how much memory must be allocated, but you cannot see the P-definition. But if you set it as a pointer, it's clear, because the pointer itself is the size compiler knows.
Copy Code code as follows:
#include <string>
#include <memory>
Class Peopleimpl;
Class Date;
Class Image;
Class people{
Public
People (const std::string & name, const date& brithday, const Image &img);
std::string name () const;
Date birthdate () const;
IMGE img () const;
...
Private
Peopleimpl * PIMPL;
}
Peopleimpl contains the following three data, and the people member variable pointer points to the PEOPLEIMPL, the compiler now knows the size of its allocated space and the size of a pointer by people definition.
Copy Code code as follows:
Public Peopleimpl
{
Public
Peopleimple (...)
...
Private
Std::string thename; Name
Date thebirthdate; Birthday
Image img; Image
}
In this way, people is completely detached from the implementation of date, imge, and people, and none of these modifications will require the people file to be recompiled. In addition this writing strengthens the encapsulation. This also reduces the dependency of the file.
Here's a summary of how to reduce the dependencies:
1. Do not use class definitions if they can be class declarations.
2. The data is represented by a pointer to the data.
3. Provide different header files for declarative and defined types.
These two files must be consistent, and if a declaration has been changed, two files will have to change. So there's usually a #include a declaration file instead of a predecessor declaring several functions.
It's like people.
Copy Code code as follows:
#include "People.h"
#include "PeopleImpl.h"
People::P eople (const std::string& name, const date& brithday, const image& IMG)
:p Impl (New Personimpl (NAME,BRITHDAY,ADDR))
{ }
std::string people::name () const
{
return Pimpl->name ();
}
Another type of handle is to make people a special abstract base class called the interface class. See interface This keyword may be familiar with C #, Java students may have dawned. This interface has no member variables, no constructors, only a virtual destructor, and a set of pure virtual functions to represent the entire interface. The interface class written for people looks like this.
Copy Code code as follows:
Class people{
Public
Virtual ~people ();
Virtual std::string name () const = 0;
Virtual Date brithdate () const = 0;
Virtual Image address () const = 0;
...
};
How do you create objects? They typically call a special function. Such functions are often referred to as factory functions or fictitious functions. They return pointers to dynamically allocated objects that support the interfaces of the interface class.
Copy Code code as follows:
class People {
Public
...
Static people* Create (const std::string& name,const date& brithday, const image& IMG);
};
The class that supports the interface class interface must be defined, and the real constructor must be called
Copy Code code as follows:
Class Realpeople:public people{
Public
Realpeople (const std::string& name,const date& birthday,const Img)
: thename (name), Thebrithdate (Brithday), theimg (IMG)
{}
Virtual ~realpeople () {}
std::string name () const;
Date birthdate () const;
Image img () const;
Private
Std::string thename;
Date thebirthdate;
Image theimg;
}
With the Realpeople class, we people::create can write this
Copy Code code as follows:
people* people::create (const std::string& name, const date& birthday, const image& IMG)
{
Return static_cast<people *> (new Realperson (name,birthday,img));
}
The handle class and the interface class remove the coupling between the interface and the implementation, thus reducing the compilation dependencies between the files. But it also loses some of the performance and space.