Effective C ++ 2e: Turn from C to C ++

xiaoxiao2021-03-06  74

From C to C for everyone, habits C take some time, which is especially very distressed for programmers that have been familiar with C. Because C is a subset of C , all C technology can continue to use, but many use is not suitable. For example, the C programmer will think of the pointer of the pointer looks quirky, they will ask: Why don't you replace the reference to the pointer? C is a simple language. It truly provides only macro, pointers, structures, arrays, and functions. No matter what problem, C is resolved by macro, pointer, structure, array, and functions. And C is not the case. Macros, pointers, structures, arrays, and functions, there are certainly, there are also private and protected members, function overload, default parameters, constructors, and destructor, custom operator, inline function, reference, friend, Template, exception, name space, etc. With C , it has a broader space with C because more choices can be considered when designing. In the face of so many options, many C programmers are in order to hold a regulations and insist on their old habits. In general, this is not a big sin. But some C habits are contrary to the spiritual nature of C , and they are elaborated in the following terms. Terms 1: Try to use const and inline without #define this terms are best known as: "Try to use compilers without pre-processes", because #define is often considered to be part of the language itself. This is one of the problems. Look at the following statement: #define aspect_ratio 1.653 compiler will never see the aspect_ratio of this symbol name, because before the source code enters the compiler, it will be removed by the preprocessor, so AsPect_Ratio does not join the symbol list. If the code involved in this constant is compiled, it will be aware of because the error information refers to 1.653 instead of aspect_ratio. If Aspect_Ratio is not defined in your own header file, you will be strange 1.653 where to come, even spend time tracking. This issue will also appear in the symbol debugger, because the same, the symbol name you wrote will not appear in the symbol list. Solution to this problem is simple: do not need to preprocess macro, define a constant: const double assect_ratio = 1.653; this method is effective. But there are two special circumstances to pay attention. First, it will be a bit different when defining a pointer constant. Because constant definitions are typically placed in the header file (many source files will contain it), in addition to the type referred to by the pointer to be defined as consts, the pointer is often defined as const. For example, to define a Char *-based string constant in the header file, you have to write twice const: const char * const authorname = "scott meyers"; About the meaning of Const, especially the problem associated with the pointer See Terms 21.

In addition, it is also very convenient to define a class (Class), only a little difference. To limit the constant to the class, you must first make it a member of the class; in order to ensure the maximum copy, you have to define it as a static member: class gameplayer {private: static const Int num_turns = 5; // constant Declaration int Scores [// use of constant ...}; there is a little, as you can see, the above statement is Num_Turns declaration, not the definition, so you must also implement the code file in the class Static members of the definition: const Int gameplayer :: num_turns; // mandatory definition; // goes in class impl. File You don't have to worry too much. If you forget the definition, the linker will remind you. Older compilers do not accept this grammar because it thinks that the static member of the class defines the initial value when the declaration is illegal; and only the initial integer type (eg int, bool, char, etc.) is allowed within the class. Can only be constant. In the case where the above syntax cannot be used, you can define the initial value: Class EngineeringConstants {// this goes in the class private: // Header File Static const Double fulge_factor; ...}; // this goes in the Class Implementation File Const Double EngineeringConstants :: fudge_factor = 1.35; Most of the cases you just do so much. The only exception is that when your class needs to use the constant of this class, for example, the declaration of the GamePlayer :: Scores array (during the compilation process must know the size of the array). Therefore, in order to make up for the insufficient compiler of the compiler in which the intersection of the intersection of the entire class is prohibited, it can be used to solve the method called "borrowing ENUM". This technology uses the principle of enumeration type when you need an int type, so GamePlayer can define this: Class GamePlayer {private: enum {num_turns = 5}; // "The enum hat" Makes // Num_Turns A Symbolic Name // for 5 int Scores [Num_Turns]; // Fine ...}; Unless you are using the old compiler (ie, you don't have to borrow ENUM. Of course, I know that this method is still worth it, because this code that can be traced back to the era of a long time is uncommon. Go back to the topic of the pretreated. Another common #define directive usage is to use it to implement macros that look like a function and does not cause function calls.

A typical example is to calculate the maximum value of two objects: #define max (a, b) ((a)> (b)? (A): (b)) This statement has a lot of defects, and the light is thinking about it. It is even more painful than driving the highway at the peak time. Whenever you write a macro like this, you have to remember that you have to add parentheses to each parameter when writing the body; otherwise, if you call your macro, if you use the expression, you will cause a lot of trouble. . But even if you are doing this, there will be a strange thing like this: int A = 5, b = 0; max ( a, b); // a value increased by 2 Max ( ) A, B 10); // A value only increases this case, what happens inside the MAX depending on what it is more than it! Fortunately, you don't have to endure such a stupid statement. You can use the normal function to achieve macro efficiency, plus the expected behavior and type security, this is the inline function (see clause 33): Inline int max (int A, int b) {RETURN A> B? A: B;} However, this is not large, because this version of MAX can only process int types. But the template can solve this problem very lightly: Template Inline const t & max (const t & a, const t & b) {RETURN A> B? A: B;} This template generates a full set of functions, each function Take two objects that can be converted into the same type of object to compare and return a reference to a large (constant) object. Because I don't know the type of T, the transfer reference can increase efficiency (see clause 22). By the way, check the standard library when you plan to write like Max, first check the standard library (see Terms 49), see if they already exist. For example, the Max saying above, you will surprise you can find a cold: max is part of the C standard library. With const and inline, you need to decrease the pretreatment, but you can't do it completely. Abandoning #include's days is still far away, # ifdef / # ifndef also plays an important role in the process of controlling compilation. Preprocessing can't retire, but you must plan to give it a long holiday.

Terms 2: Try to use without , Scanf and Printf are very light, very efficient, you have long you know how to use them, this I admit. But although they are useful, in fact, Scanf and Printf and their series can also do some improvements. In particular, they are not type safe and there is no scalability. Because type security and scalability are the cornerstone of C , you have to obey this. In addition, the Scanf / Printf series function separates the variables to be read and the information on the information on the read / write format, just like ancient Fortran. It is time to say to the fifth year! Don't be amazed, these weaknesses of Scanf / Printf are interatrays >> and << "strength: int I; Rational R; // r is a constant ... CIN >> I >> R; cout << i << r The above code must be compiled, >>, and << must be the overload function of the Rational type object (may be converted by implicit type). If such a function is not implemented, it will be wrong (handling Int does not do this because it is a standard usage). In addition, the compiler can select different forms of operators according to different variable types, so it is not necessary to deserve that the first object to be read is Int, and the second is Rational. In addition, the grammar form is the same as the syntax form used when reading and writing objects, so it is not necessary to remember some of the SCANF, for example, if the pointer is not obtained, addicts, and if the pointer has been, it is necessary to add it. Address. These can be handed over to the C compiler. There is nothing else to do anything else, but you are different. Finally, it is important to note that the custom type like INT and the custom type of Rational Rational is the same. And you will try it with Scanf and Printf! The code you have written to indicate the category of the rational class like this: class russ {public: russional (int name = 0, int devenator = 1); ... private: int N, d; // molecule, skeleton Friend Ostream & Operator << (Ostream & S, Const Rational & R);}; Ostream & Operator << (Ostream & S, Const Rational & R) {S << RN << '/' << rd; return s;} The above code involves Operator << Some subtle (but very important) usage, which is discussed in detail in this book. For example: The above Operator << is not a member function (Terms 19 explains why), and it is not a reference to operator <<, but is not a reference to the object of Const (see Terms 22). The statement and implementation of Operator >> are similar. Although I am not willing to admit it, it is still very meaningful to return to those who have proved and correct old roads.

First, some iostream's operations are achieved less than the corresponding C stream efficiency, so different options will give you the program (although not necessarily, see the Terms M16) brings a lot. But please keep in mind that this is not for all Iostream, just some special implementation; see Terms M23. Second, in the standardization process, the iostream library has made a lot of modifications in the underlying (see Terms 49), so for those applications that require maximum portability, different manufacturers will follow the standards. Third, the functionality of the iostream library is constructed and the function in does not, when some sequence involves the initialization of static objects, if it is confirmed that there is no hidden danger, use the standard C library more simple and practical. . The value of the type of security and scalability provided by the Iostream library and functions are far more than your original imagination, so don't just discard it just because you are used to . After all, after the transition to iostream, you will not forget . By the way, the title of this Territor has not been printed; I do is rather than . It is technically said that there is no this - the Standardization Commission replaces it with when simplifying the non-C standard header file. The reason they do is explained in terms 49. It must also be known that if the compiler supports and , the use of the file name will be very subtle. For example, if #include is used, it is the element of the iostream library placed under the namespace STD (see Terms 28); if #include is used, it is the same as the global space. Elements. Acquiring elements in global space can lead to name conflicts, while the original intention of design name space is used to avoid this conflict in this name. Also, when typing, is more than less words, this is also why many people use it. :)

Terms 3: Try to use new and delete without Malloc and Freemalloc and Free (and its variants) will have problems in the fact that they are too simple: they don't know constructor and destructive functions. Suppose uses two ways to allocate a space containing 10 String objects, one with malloc, another with new: string * stringArray1 = static_cast (malloc (10 * sizeof (string)); string * stringArray2 = New String [10]; The result is that StringArray1 does point to sufficient space that can accommodate 10 String objects, but these objects are not created in memory. Moreover, if you don't jump from this obscure symbol (see the description of clauses M4 and M8), you have no way to initialize the objects in the array. In other words, StringArray1 is actually not used. Instead, StringArray2 points to an array containing 10 fully constructive String objects, each of which can be safely used in any read string. Suppose you think of a strange trick to initialize the objects in the StringArray1 array, then you will do this in the latter program: free (StringArray1); delete [] stringArray2; // See Terms 5: Why is this The last "[]" call free will release the memory points to the StringArray1, but the String object in the memory does not call the destructor. If the String object is in general, you have allocated memory, and these memory will be lost. Conversely, each object in the array will call the destructor before memory release when calling DELETE. Since New and Delete can be interactively interact with constructor and destructive functions such as constructor, they are obvious. It is also a bad idea to mix new and delete with Malloc and Free. Call free with a pointer for NEW, or call Delete to call with Malloc, which is unpredictable. Everyone knows "unpredictable" means that it may work well in the development phase, work well during the test phase, but may also be in the face of your most important customer's face. The incompatibility between New / Delete and Malloc / Free often leads to some serious complexity issues. For example, usually has a strDup function, it gets a char * string and then returns its copy: char * strdup (const char * ps); // Return to the copy of the PS in some places, C and C are the same STRDUP version, so the function is allocated with MalloC. In this case, some uninformed C programmers will ignore the pointer returned to the strDUp for free operation after calling StrDUP. In order to prevent this, some places will rewrite StrDup for C , and new NEW is called inside the function, which requires its caller to remember the last use of Delete. You can imagine, this will cause how serious transplantability, because StrDUP in the code is in different places in different places. C programmers and C programmers are also very interested in reused code. Everyone knows that there is a large number of C libumes composed of codes written by Malloc and Free.

转载请注明原文地址:https://www.9cbs.com/read-90579.html

New Post(0)