If you think that the time to re-compile the file is short or long, it doesn't matter if you need to re-compile the file, you can skip this article, but you are also advised to browse it.
If you want to learn or care about this content, this article will surely bring you some benefits.
First, I will not define the dependency. I will give an example.
Copy codeThe Code is as follows: class objective El {
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 the class string, Date, and Image, class People cannot be compiled. Generally, this definition is provided by the # include header file. Therefore, these preprocessing commands are generally provided on People.Copy codeThe Code is as follows: # include <string>
# Include "date. h"
# Inblude "image. h"
Class cancel {
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
};
In this way, a compilation dependency is formed between the People definition file and the three files. If any one of these header files is changed, or the header files depend on any other header files, each file containing the People class needs to be re-compiled and the People class file needs to be re-compiled. Think about it. If a project contains thousands of files, each of which contains several other files, and in turn changes the content of a file, the entire project needs to be re-compiled, it can be said that it is too bad.
We can make the following changes:
Copy codeThe Code is as follows: namespace std {
Class string;
}
Class Date;
Class Image;
Class cancel {
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
};
In this way, only the People interface will be re-compiled when it is changed. However, in this case, the first string is not a class, and it is a typedef basic_string <char> string. Therefore, the preceding pre-declaration is incorrect (with its full code in stl). Correct pre-declaration is complicated. In fact, for the standard library, we only need to include the # include preprocessing command.Copy codeThe Code is 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 pre-declaration is that the compiler must know the object size during compilation to allocate space.
For example:Copy codeThe Code is 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 the p definition cannot. However, if it is set as a pointer, it is clear that the compiler knows the size of the pointer itself.Copy codeThe Code is as follows: # include <string>
# Include <memory>
Class leleimpl;
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 member variable pointer of People points to this PeopleImpl. Now the compiler knows the size of the allocated space through the definition of People, and the size of a pointer.Copy codeThe Code is as follows: public PeopleImpl
{
Public:
PeopleImple (...)
...
Private:
Std: string theName; // name
Date theBirthDate; // birthday
Image img; // Image
}
In this way, People is completely separated from the implementations of Date, Imge, and People. No modifications to the above classes need to be re-compiled to the People file. In addition, this write method enhances encapsulation. This reduces the file dependency.
Here we will summarize the ways to reduce dependency:
1. If you can declare a class, do not use the class definition.
2. Use a pointer to the data.
3. Different header files are provided for declarative and defined headers.
The two files must be consistent. If one declarative file is changed, both files must be changed. Therefore, there is usually a # include declaration file instead of a number of pre-declaration functions.
Set like People
Copy codeThe Code is as follows: # include "People. h"
# Include "leleimpl. h"
People: People (const std: string & name, const Date & brithday, const Image & Img)
: PImpl (new PersonImpl (name, brithday, addr ))
{}
Std: string People: name () const
{
Return pImpl-> name ();
}
Another Handle method is to make People a special abstract base class called Interface class. The keyword "interface" may be familiar with C # and java. This interface does not contain member variables or constructor. It has only one virtual destructor and a set of pure virtual functions to represent the entire interface. The interface class written for People looks like this.Copy codeThe Code is as follows: class People {
Public:
Virtual ~ People ();
Virtual std: string name () const = 0;
Virtual Date brithDate () const = 0;
Virtual Image address () const = 0;
...
};
How to create an object? They usually call a special function. Such a function is usually called a factory function or a virtual constructor. They return pointers to dynamically allocated objects, which support interfaces of the interface class.Copy codeThe Code is as follows: class People {
Public:
...
Static People * create (const std: string & name, const Date & brithday, const Image & Img );
};
The class that supports interface interfaces must be defined and the real constructor must be called.Copy codeThe Code is as follows: class RealPeople: public People {
Public:
RealPeople (const std: string & name, const Date & birthday, const Image & 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 can write People: create in this way.Copy codeThe Code is 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 relationship between interfaces and implementations, thus reducing the compilation dependency between files. However, it also consumes some performance and space.