[HERB SUTTER's name "More Exceptional C Chinese version will be published soon. As a translator of this book, I am very happy to recommend this book to everyone. Judging from Huazhong University of Science and Technology Press agreed, I will disclose some translation, please ask everyone to criticize. ]
Wild program design and C standard library
One of the powerful features of C is the support of generic programing. This power is directly reflected in the flexibility of the C standard library, especially its container, iterator, and algorithm part, which has always been referred to as a standard template library (STL).
This book is discussed to discuss how to use the C standard library most effectively, especially STL. When will I use Std :: Vector and Std :: Deque? how to use? Which traps may I encounter when using std :: map and std :: set? How to safely avoid these traps? Std: remove () Why can't I really delete anything?
This chapter also introduces some useful techniques and incorrect mistakes. When writing your own generic program code, you include the code that "working with STL" or "to expand STL" code, you They will often encounter them. What kind of Predicates can be used safely with STL? What kind of can't it do? why? To make the behavior of the template itself can change, and this behavior changes are based on the ability to "Type", what existing technology can write this powerful generic template code. ? How to switch between different types of input and output streams? What is the template specialization and overload? What is the "weird" TypenAme keyword?
With the in-depth study of generic programming and C standard libraries, we will also encounter more problems.
Terms 1: Flow Difficulties: 2 What is the best way of use when using different input and output streams, including standard controls?
1. What is the type of std :: cin and std :: cout?
2. Write an Echo program to simply respond to the input, and can be equivalently called by the following two ways:
Echo
Echo infile outfile
In most popular command line environments, the first command assumes that the program gets input from CIN and sends the output to COUT. The second command tells the program from a file called Infile and generates an output in a file named Outfile. This program should be able to support all over the input / output options.
answer
1. What is the type of std :: cin and std :: cout?
A short answer, CIN is actually:
Std :: basic_istream
COUT is actually:
STD: Basic_OStream
Here is a more detailed answer, which shows you the answer from the answer to you through some standard TypedEf and templates. First, CIN and COUT have the types of std :: istream and std :: Ostream. Next, these types are std :: basic_istream
2. Write an Echo program to simply respond to the input, and can be equivalently called by the following two ways:
Echo
Echo infile outfile
Maximistic solution
For those who pursue streamlined code, the most streamlined program is too much below, it contains only one statement:
// Example 1-1: Surprised? Only use a statement
//
#include
#include
Int main (int Argc, char * argv [])
{
Using namespace std;
(Argc> 2
? OFSTream (argv [2], ios :: out | os :: binary)
: cout)
<<
(Argc> 1
? IFStream (Argv [1], iOS :: in | ios :: binary)
: cin)
.rdbuf ();
}
This program is feasible, thanks to two complementary conditions: First, Basic_ios provides a convenient RDBUF () member function, which returns the streambuf used by a stream object, in this case, this stream object is also It is a CIN or a temporary IFSTREAM object, both of which are derived from Basic_ios. Second, Basic_OStream provides an operator << (), which is accepting such a Basic_StreamBuf object, as an input, and then reads full read. As the French will say, "Ca" ("That's this!").
Gradually tend to be more flexible
There are two main disadvantages in the scenarios in Example 1-1: First, the refinement will bring 涩, and excessive streamlined is not suitable for application to product code.
Design guidelines
Try to improve readability. Avoid writing a streamlined code (ie, simple but difficult to understand and maintain). Avoid you.
Second, although Example 1-1 answered the previous problem, this method is only possible to copy the input. This feature may already be enough, but if you need to do other processing in the future, such as converting characters to uppercase, or calculating the total number of characters, or deleting a third character, what should I do? This need is very reasonable in the future; so, it is best to do it now, put these processing work in a separate function, make this function can use the correct type of input or output objects in Polymorphically. :
#include
#include
INT Main (int Argc, char * argv []) {
Using namespace std;
FSTREAM IN, OUT;
IF (Argc> 1) in .open (Argv [1], ios :: in | os :: binary);
IF (Argc> 2) Out.Open (Argv [2], iOS :: out | ios :: binary);
Process (In.is_open ()? In: cin,
OUT.IS_Open ()? OUT: COUT);
}
But how to implement process ()? In C , there are four ways to get polymorphism: virtual functions, templates, overloads, and conversions. Among them, the first two methods can be used directly here to express the polymorphics we need.
Method A: Template (compile time polymorphism)
The first method is used to compile timeal polymorphism, which requires a template; it only needs to be passed with a suitable interface (eg, a member function called RDBUF ():
// Examples 1-2 (a): Templated Process ()
//
Template
Void Process (In & In, Out & Out) {
// ... Execute some more complex operation,
// or just a simple "OUT << in.rdbuf ();" ...
}
Method B: virtual function (running multiple)
The second method is used by running polymorphism, which requires a condition that there is a public base class with a suitable interface:
// Examples 1-2 (b): The first attempt is feasible to a certain extent
//
Void Process (Basic_istream
Basic_ostream
{
// ... Execute some more complex operation,
// or just a simple "OUT << in.rdbuf ();" ...
}
Note that in Examples 1-2 (b), the parameter type of Process () is not BASIC_IOS
There is no doubt that the method in Examples 1-2 (b) has dependence, which requires input and output streams to derive from Basic_istream
Therefore, even if the method B is used, we should also use the template to make the compiler to derive the appropriate parameters:
// Example 1-2 (c): Better solution
//
Template
Void Process (Basic_istream
Basic_ostream
{
// ... Execute some more complex operation,
// or just a simple "OUT << in.rdbuf ();" ...}
Effective engineering design principle
In terms of it, all the above answers are "correct"; but in the current occasion, I personally prefer the method A. The reason is attributed to two very valuable design guidelines. The first one is:
Design guidelines
Try to improve the expansion.
Avoid writing code can only solve the current problem. Almost anytime, if you can write an expandable program, it will be better choice - of course, as long as we are not too much.
Balanced judgment is a feature of experienced programmers. In particular, in "writing special code, solving only current problems" (short, it is difficult to expand) and "write a grand generic frame to solve the problem" (pursue over-design), experienced programmer Know how to get the best balance.
Compared with the scheme in Example 1-1, method A has substantially the same overall complexity, but in addition, the latter is more readily understood and more expandable. Compared with the method B, method A is simple and more flexible; it can adapt to new requirements because it is not bonded, not just dealing with the iostream system.
So, if there are two options, they have the same amount of work in design and implementation, and have roughly comparable clarity and maintainability, then please consider the expandability as much as possible. This suggestion is not to teach you, let you go to a very simple problem - this area has been much more enough. On the contrary, this suggestion is a encouragement: If you think about it, you can find that your problem is actually a special case of a more common problem, you should do more work, not just to solve the current problem. . This suggestion is very correct, because the expansion of expansion is improved in the design, and it is often meant to increase encapsulation.
Design guidelines
Try to improve encapsulation. Separate the relationship.
As long as it is possible, a piece of code-function or class should only know and only responsible for one thing.
It can be proved that the best place for method A is that it exhibits effective separation between relations. It includes two partial code, part of the code knows the possible difference in the input / output source (Source) and the target (SINK), and another part of the code knows how to actually perform processing, which is separated from the two part of the code. This separation also makes the code's use is clearer, easier to read and understand others. Effective separation of relationship is another feature of engineering design. In this book terms, we will continue to see this.