This is a very classic C + + books, but also I found myself in the work of C + + has a lot of weak points when often brought to charge. This book is a lot of content, said a lot of how to use C + + methods, some places themselves have not been able to chew, read once a lot of knowledge points easy to forget, this is a piece of the study, the reason will be shared out is that for programmers, good memory than bad writing, Self-action often in the real writing process can be like a reflex to write good code. Slow work out deliberately, the treatment of technology should be cautious and awe.
This book recommended like C + + people read more, on the text of some ideas can be self-practice, because the content is more, so according to the section of the Division, the notes will have some doubts, but also hope that we can help me to answer, a lot of communication can be improved.
Guide
Terms
Declarative (Declaration): This tells the compiler the name and type, omitting the details.
Here are a few common definitions:
extern int x; //具体的原因可以看扩展知识部分std::size_t numDigits(int number);class Widget;template<typename T>;class GraphNode;
definition: Provides details of compiler-declared omission (the compiler allocates memory for the variable).
int x;std::size_t numDigits(int number){ //your codes...}
explicit is used to prevent implicit type conversions (implicit type conversions), which can be used for explicit type conversions (explicit type conversions). So when declaring the constructor, if there is no need for implicit type conversion, declare as explicit as possible. ( Here's a question, many of the constructors in the C + + game engine are not declared as explicit?)
The difference between a copy constructor and a copy assignment function I believe you should be familiar with it, a small point to note that using the "=" syntax can also be used to invoke the copy constructor.
Widget w1; //default构造函数Widget w2(w1); //copy构造函数w1 = w2; //copy assignment操作符Widget w3 = w2; //copy构造函数
Expand your knowledge
(1) size_t is just a typedef, which is a non-signed type used in C + + calculation number.
(2) extern placed before a function or variable to indicate that the function and the variable is defined in another file, the compiler will seek to define from other modules, that is, the extern declaration of the variable can only be defined , and do not allocate memory, find the variable or function of the declaration to allocate memory.
(3) extern can also be used to specify the link, the common use is extern "C", because C + + at compile time in order to solve the function of the polymorphic problem, will be the functions of the name and parameters together to generate an intermediate function name, and C language will not, Therefore, when the function is not found in the link stage, the C function needs to use the extern "C" for the link designation. If you want to know how the process of linking, you can go to see the "self-cultivation of the programmer" This book, there is a very detailed explanation, this book I have not eaten thoroughly, not more wordy.
(4) the keyword corresponding to extern is static, and the global variables and functions it modifies can only be used in this module. Therefore, a function or variable may not be modified by extern "C" only if it is used by this module.
(5) common usage of extern:
#ifdef __cplusplusextern "C" {#endif/insert you code here.../#ifdef __cplusplus}#endif
Clause I
Article Two
Use # define as little as possible, and you can replace it with Const,enum,inline. #define的缺点:
(1) debugging problem : Assuming # define Aspect_ratio 1.653, when the compiler starts processing the source code, the preprocessor has removed the aspect_ratio, so there is no aspect_ in the symbol table RATIO, you can only see a maximum of 1.653 when debugging compilation error messages.
(2) scope problem : We cannot create a class's exclusive constant with # define, once the macro is defined, the subsequent compilation process will be valid without any encapsulation.
(3) One of the great benefits of macros is that when you define a function with a macro, it does not lead to additional overhead due to function calls, but the disadvantage of this macro is obvious.
#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))int a = 5,b = 0;CALL_WITH_MAX(++a,b); //a被累加2次CALL_WITH_MAX(++a,b); //a被累加1次
This time, if you use template inline, you can not only gain the efficiency of the macro, but also ensure the security of the function.
template<typename T>inline void callWithMax(const T& a,const T& b){ f(a > b ? a : b);}
Expand your knowledge
(1) class's exclusive constant, so that the initialization of a dedicated constant is a lot of restrictions, in order to ensure that the scope of the constant is limited to the class, you must make it a member of class, and in order to ensure that the constant can only have one copy of the entity, You have to make it a static member (in order to avoid complex compiler rules, C + + requires that each object can have only a single definition, and if C + + allows you to define an entity that occupies the same memory as an object within a class, that rule is destroyed).
class GamePlayer {private: static const int NumTurns = 5; //常量声明式 int scores[NumTurns]; //使用该常量 ...};
Note that this is just a constant declarative rather than a definition, and usually C + + requires you to provide a definition for anything you use, but special handling is required if it is a class-specific variable that is static and is an integer type (for example, Ints,chars,bools). as long as you don't take their addresses , you can declare and use them without having to provide a definition.
Another way you can do this is by using an enum:
class GamePlayer {private: enum { NumTurns = 5 }; int scores[NumTurns]; ...};
At this point the enum can be ints, which means that the constant must be an integer or enum type initialized by a constant expression. One advantage of doing this is that others cannot get a pointer or reference point to this integer constant.
Article Three
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 modeled as a pointer, so the iterator acts like a t* pointer.
Declaring an iterator as const is like declaring a pointer as const (T * const), and if you want the iterator to point to something that is immutable (the 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;
The meaning of the const member function:
(1) Make the interface of the function easier to understand;
(2) It is possible to manipulate a const object.
Two member functions can be overloaded if they are just a constant variable. The member function is const, which means bitwise constness and logical constness.
Bitwise constness is a constant definition of C + +, a const member function is not allowed to change any not-static member variable within an object. This is also enforced by the compiler, the compiler will only go to find the member variable assignment action, the following practice 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‘; // 这里编译器的语法检查是不会报错的,但编译的时候就会报错
The problems that are often encountered in the actual use of the const member function are as follows:
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 can't modify a non-static variable in a const member function, so how do you get rid of that constraint? Workaround--mutable (variable).
class CTextBlock { ... mutable std::size_t textLength; mutable bool lengthIsValid; ...};
The repetition of const and NON-CONST member functions cannot be solved by mutable, for example:
class CTextBlock {public: const char& operator[](std::size_t position) const { ...//边界检查 ...//记录数据访问 ...//检验数据完整性 return text[position]; } char& operator[](std::size_t position) { ...//边界检查 ...//记录数据访问 ...//检验数据完整性 return text[position]; }private: std::string text;};
The way to solve the problem is to transform:
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;};
There were two transformations, the first being to add a const to *this (call the const version of operate[]) and the second to remove the const from the return value of the const operator[].
Article IV
Typically, if you use C part of C + +, the initialization will definitely lead to the cost of the run-time, then there is no guarantee of initialization, non-c parts of C + + can guarantee this. (This is why the array does not guarantee that its contents are initialized, and vector has this guarantee)
In addition to the built-in types, initialization is done by constructors, to ensure that each constructor can initialize each member of the object. It is important to note that assignment and initialization cannot be confused .
ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones){ theName = name; // 赋值,而非初始化 theAddress = address; thePhones = phones; numTimesConsulted = 0;}
C + + stipulates that the initialization action of a member variable of an object occurs before entering the constructor body , that is, it is actually an assignment operation within the constructor, and the initialization action occurs when the default constructor of the member variable (not the built-in type) is invoked. So the usual initialization method usually takes the initialization list.
ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones): theName(name), // 初始化操作theAddress(address),thePhones(phones),numTimesConsulted(0){}
This is more efficient because, based on the assignment version, the default constructor is called first to set the initial value for Thename,theaddress and Thephones, and then the assignment is immediately re-assigned, and the method that initializes the list passes the argument directly to the constructor. Equivalent to only one call to the copy constructor .
For most types, it is more efficient to call the copy constructor only once, than at first calling the default constructor and then invoking the copy assignment operator . In summary, use the member initial list, simple and efficient. For those that have many member variables and base classes, or if the initial value of a member variable is read by a file or database, you can put some initialization operations into the private function for the constructor to call, avoiding duplication of initialization. (init function in COCOS2DX)
The member initialization order of C + + is fixed, the base classes is initialized earlier than derived classes, and the class's member variables are always initialized in their declaration order (note declarative), even if the order occurs in the initialization list.
the initialization order of non-local static objects defined in different compilation units is undefined :
(1) The static object includes the global object, the object that defines the domain namespace scope, the object declared as static within the classes, within the function, and within the file scope. A static object within a function can be called a local static object, and other static objects are called Non-local satic objects. At the end of the program, the static object is automatically destroyed, and the destructor is automatically called at the end of Main ().
(2) The compilation unit refers to the output of a single target document source code, basically a single source file plus included header files .
Non-local static objects within multiple compilation units are formed by "template functions are present" and cannot determine the correct initialization order. So how do we solve this problem?
A simple method in design mode solves this problem by placing each non-local static object in its own exclusive function (which is declared as static in this function), which returns a reference to the object to which it points. The user then calls these functions without directly referring to those objects. Replace the Non-local static object with the local static object because the local static object is initialized during the function call or the first time the definition is encountered, and the reference returned by the function guarantees that the object has been initialized. And if this function is not called, the cost of construction and destruction will not be raised. This is a common implementation of the singleton pattern .
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); // 如何保证tfs在tempDir之前初始化class FileSystem { ... };FileSystem& tfs() { static FileSystem fs; // 用函数代替tfs,返回一个reference指向local static对象 return fs;}class Directory { ... };Directory::Directory(params) { ... std::size_t disks = tfs().numDisks(); ...}Directory& tempDir(){ static Directory td; // 同理 return td;}
This structure is very simple, define and initialize a local static object and then return, if used frequently, you can make them a inlining function. For embedded static objects, there are uncertainties in multithreaded systems, so you can call all Reference-returning functions manually during the single-threaded startup phase.
To summarize, the initialization of the object:
(1) Manual initialization of the built-in Non-member object;
(2) Use the initialization list to process all components of the object;
(3) In the case of uncertain initialization order, strengthen their own design.
Expand your knowledge
(1) about C + + compiler and run-time issues, in the "Deep Exploration of C + + object Model" This book's reading notes and introduce you again.
Reading notes _effective c++_ customary C + +