Reading Notes _ positive tive C ++ _ habits C ++, Reading Notes _ positive

Source: Internet
Author: User

Reading Notes _ positive tive C ++ _ habits C ++, Reading Notes _ positive

This is a very classic C ++ book, and I often use it for charging when I find many weak points in C ++ at work. This book has a lot of content and tells you a lot about how to use C ++ efficiently. In some places, you cannot understand it yourself. After reading it, many knowledge points are easy to forget, this is a review in one place. I think that for programmers, good memory is not as bad as writing, when writing a program, you can write the code as well as the conditional reflection. If you work slowly, you must be cautious and awe-inspiring when dealing with technology.

This book recommends that C ++ users read more and practice some of the ideas in this article on their own. Because there are a lot of content, the sections are divided and there will be some questions in the notes, I also hope that you can help me solve the problem and improve your communication skills.

Guide

Terms

Declarative): This tells the compiler name and type, skipping the details.

Common definitions:

Extern int x; // For details, refer to the extended knowledge section std: size_t numDigits (int number); class Widget; template <typename T>; class GraphNode;

Definition): Provides details of compiler declarative omissions (the compiler allocates memory for the variables ).

int x;std::size_t numDigits(int number){    //your codes...}

ExplicitIt is used to prevent implicit type conversions from being used for explicit conversions ), therefore, if you do not need implicit type conversion when declaring the constructor, try to declare it as explicit. (Most constructor in many C ++ game engines are not declared as explicit?)

The difference between the copy constructor and the copy value assignment function should be familiar to everyone. You should note that the "=" syntax can also be used to call the copy constructor.

Widget w1; // default constructor Widget w2 (w1); // copy constructor w1 = w2; // copy assignment operator Widget w3 = w2; // copy constructor



Extended knowledge

(1) size_t is just a typedef. It is a type used in C ++ to calculate the number without positive or negative signs.

(2) If extern is placed before a function or variable, it indicates that the function and variable definition are in another file. The compiler will seek definitions from other modules, that is, the variables declared by extern can only beDefinitionNo memory is allocated. The memory can be allocated only when the declared variable or function is found.

(3) extern can also be used to specify links. The common usage is extern "C", because C ++ solves the problem of function polymorphism during compilation, the function name and parameter are combined to generate an intermediate function name, but the C language does not. Therefore, the function cannot be found during the link stage, in this case, the C function needs to use the extern "C" to specify the link. If you want to know how to do this during the link process, you can go to the "programmer's self-cultivation" book, which has a very detailed explanation. I have not yet thoroughly understood this book, it won't be too long.

(4) The keyword corresponding to extern is static. The global variables and functions modified by it can only be used in this module. Therefore, a function or variable can only be used by this module and cannot be modified by extern "C.

(5)Common Use of extern:

#ifdef __cplusplusextern "C" {#endif/insert you code here.../#ifdef __cplusplus}#endif


Clause 1

Clause 2

Use # define as much as possible. You can use const, enum, and inline to replace it. # Define disadvantages:

(1)Debugging problems: Suppose # define ASPECT_RATIO 1.653, when the compiler starts to process the source code, the pre-processor has removed ASPECT_RATIO, so there is no ASPECT_RATIO at the root of the symbol table, you can only view a maximum of 1.653 error messages during debugging and compilation.

(2)Scope Problems: We cannot use # define to create a class-specific constant. Once a macro is defined, the subsequent compilation process will be effective without any encapsulation.

(3) one of the major advantages of macros is that when a macro is used to define a function, it will not cause additional overhead caused by function calls, but will improve the efficiency, however, the disadvantages of this macro are also obvious.

# Define CALL_WITH_MAX (a, B) f (a)> (B )? (A): (B) int a = 5, B = 0; CALL_WITH_MAX (++ a, B); // a is accumulated twice CALL_WITH_MAX (++, b); // a is accumulated once

In this case, if you use template inline, you can not only improve the efficiency of macros, but also ensure the security of functions.

template<typename T>inline void callWithMax(const T& a,const T& b){    f(a > b ? a : b);}


Extended knowledge

(1) Exclusive constants of the class. There are many restrictions on Initialization of such exclusive constants. To ensure that the scope of constants is restricted within the class, make it a member of the class, and make it a static member to ensure that this constant can have only one entity at most (to avoid complicated compiler rules, C ++ requires that each object can have only one separate definition. If C ++ Allows defining an object that occupies the same memory as the object in the class, such a rule is broken ).

Class GamePlayer {private: static const int NumTurns = 5; // constant declarative int scores [NumTurns]; // use this constant ...};

Note that this is just a constant declarative rather than a definition. Generally, C ++ requires you to provide a definition for everything you use, however, if it is a class exclusive variable and is static and of the integer type (such as ints, chars, bools), special processing is required.As long as their addresses are not retrievedYou can declare and use them without providing a definition.

You can also use the enum method:

class GamePlayer {private:    enum { NumTurns = 5 };    int scores[NumTurns];    ...};

In this case, enum can be used to assign the ints permission. That is to say, a constant must be an integer or enumeration type initialized by a constant expression. One advantage of this is that others cannot obtain a pointer or reference pointing to this integer constant.


Clause 3

char greeting[] = "Hello";char* p = greeting; //non-const pointer,non-const dataconst char* p = greeting;   //non-const pointer, const datachar* const p = greeting;   // const pointer, non-const dataconst char* const p = greeting; //const pointer, const data

The STL iterator is molded Based on pointers, so the iterator acts like a T * pointer.

Declaring an iterator as const is like declaring a pointer As const (T * const). If you want the iterator to point to something that cannot be changed (const T * pointer ), const_iterator is required.

std::vector<int> vec;const std::vector<int>::iterator iter = vec.begin();*iter = 10;++iter; // Error!!!std::vector<int>::const_iterator cIter = vec.begin();*cIter = 10; // Error!!!++cIter;

Const member functions:
(1) make the function interfaces easier to understand;
(2) It is possible to operate the const object.

If the two member functions have different constants, they can be overloaded. If the member function is const, it means bitwise constness and logical constness.
Bitwise constness is the definition of constants in C ++. The const member function cannot change any not-static member variables in the object. This is also enforced by the compiler. the compiler will only look for the value assignment action of member variables. The following method can bypass the syntax check.

Class CTextBlock {public: CTextBlock (char str []) {pText = str;} char & operator [] (std: size_t position) const {return pText [position];} private: char * pText;}; const CTextBlock cctb ("Hello"); char * pc = & cctb [0]; * pc = 'J '; // here, the compiler syntax check will not report errors, but will report errors during compilation.

In actual use of const member functions, the following problems often occur:

class CTextBlock {public:    CTextBlock(char str[]) { pText = str; }    std::size_t length() const;private:    char* pText;    std::size_t textLength;    bool lengthIsValid;};std::size_t CTextBlock::length() const{    if (!lengthIsValid) {        textLength = std::strlen(pText); // Error!!!        lengthIsValid = true; // Error!!!    }    return textLength;}

You cannot modify the non-static variable in the const member function. How can you get rid of this constraint? Solution: mutable ).

class CTextBlock {    ...    mutable std::size_t textLength;    mutable bool lengthIsValid;    ...};

Repeated const and non-const member functions cannot be solved through mutable, for example:

Class CTextBlock {public: const char & operator [] (std: size_t position) const {... // border check... // record data access... // verify data integrity return text [position];} char & operator [] (std: size_t position ){... // border check... // record data access... // verify data integrity return text [position];} private: std: string text ;};

The solution to the problem is transformation:

class CTextBlock {public:    const char& operator[](std::size_t position) const {        ...        return text[position];    }    char& operator[](std::size_t position) {    return const_cast<char&>(        static_cast<const CTextBlock&>(*this)[position]    );private:    std::string text;};

Two transformations are performed here. The first is to add const for * this (call const operate []), and the second is to remove const from the return value of const operator.

Clause 4

If C part of C ++ is used, initialization will definitely lead to the runtime cost, so initialization cannot be guaranteed. non-C parts of C ++ can ensure this. (That is why the array cannot guarantee that its content is initialized, but the vector does)

Initialization is performed by constructors except for built-in types. Make sure that each constructor can initialize every member of the object. Note thatThe assignment and initial values cannot be confused..

ABEntry: ABEntry (const std: string & name, const std: string & address, const std: list <PhoneNumber> & phones) {theName = name; // assign values instead of initializing theAddress = address; thePhones = phones; numTimesConsulted = 0 ;}

C ++ rules,The initialization action of the object's member variables takes place before entering the constructor body.That is to say, in the constructor, only the value assignment operation is performed. The initialization action occurs when the default constructor of the member variable (non-built-in type) is called. Therefore, the usual initialization method usually uses the initialization list.

ABEntry: ABEntry (const std: string & name, const std: string & address, const std: list <PhoneNumber> & phones): theName (name ), // initialize theAddress (address), thePhones (phones), numTimesConsulted (0 ){}

This method is more efficient because it is based on the assigned versionFirst, call the default constructor to set the initial values of theName, theAddress, and thePhones, and then immediately re-assign values. The method of initializing the list will directly pass the real parameters to the constructor, it is equivalent to calling the copy constructor only once..

For most types,Compared with calling the default constructor and then calling the copy assignment operator, it is more efficient to call the copy constructor only once.. In short, using the initial member list is simple and efficient. For those with many member variables and base classes, or the initial values of member variables are read by files or databases, some initialization operations can be put into private functions for the constructors to call, avoid repeated initialization. (Init function in cocos2dx)

The initialization sequence of C ++ members is fixed. base classes is initialized earlier than derived classes, while the class member variables always followDeclaration Order(Pay attention to declarative) initialization, even if the order in the initialization list is different.

The initialization sequence of non-local static objects defined in different compilation units is uncertain.:
(1) static objects includeGlobal object, defining objects in the namespace scope, objects declared as static within the classes, within the function, and within the file Scope. The static object in the function can be called a local static object, and other static objects are called non-local satic objects. When the program ends, the static object is automatically destroyed, and the Destructor is automatically called at the end of main.

(2) A compilation unit refers to the source code that generates a single target file, basicallyAdd the contained header file to a single source code file.

Non-local static objects in multiple compilation units are formed by "template functions are made available" and the correct initialization order cannot be determined. So how can we solve this problem?

A Simple Method in the design pattern can solve this problem:Place each non-local static object in its own exclusive function (this object is declared as static in this function). These functions return a reference pointing to the object it points. Then the user calls these functions without directly referring to these objects. Replace the non-local static object with the local static object because the local static object will be initialized during the function call or when the definition is first encountered, the reference returned by the function ensures that the object has been initialized. If the function has not been called, the construction and analysis costs will not be incurred. This is a common implementation method of the Singleton mode..

Class FileSystem {public :... std: size_t numDisks () const ;...}; extern FileSystem tfs; class Directory {public: Directory (params );...}; directory: Directory (params ){... std: size_t disks = tfs. numDisks ();...} directory tempDir (params); // how to ensure that tfs initializes class FileSystem {...} before tempDir {...}; fileSystem & tfs () {static FileSystem fs; // replace tfs with a function and return a reference pointing to the local static object return fs;} class Directory {...}; directory: Directory (params ){... std: size_t disks = tfs (). numDisks ();...} directory & tempDir () {static Directory td; // Similarly, return td ;}

This structure is very simple. It defines and initializes a local static object and then returns it. If it is frequently used, it can be used as an inlining function. For static objects, there are uncertainties in the multi-threaded system, so you can manually call all the reference-returning functions in the single-threaded startup phase.

To sum up, initialize the object:
(1) manually initialize the internal non-member object;
(2) Use the initialization list to process all the components of the object;
(3) Enhance your design in case of uncertain initialization order.

Extended knowledge

(1) For questions about the C ++ compiler and runtime, go to the reading notes in the book "Deep Exploration of C ++ object model.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.