Small Tips for C ++ learning and tips for learning
Two classic categories of Classes
Class without pointer member (s)
Complex
Class with pointer member (s)
String
Defense statement in Header
Complex. h
# Ifndef _ COMPLEX __
# Define _ COMPLEX __
// Code
# Endif
Inline function
If the function is defined in the class body, it will automatically become a candidate for the inline function.
For a function defined outside the class body, you need to add the inline keyword. We recommend that the compiler compile the function as an inline function.
Constructor (ctor, constructor)
Class complex
{
Public:
Complex (double r = 0, double I = 0) // default real Parameter
: Re (r), im (I) // Initial Value column
{}
};
Initialize variables in the initial value column in the constructor, instead of assigning values using "=" in the function body.
The construction of a variable has two phases: initialization and assignment. Therefore, the mechanism of using the initial value column is more efficient (eliminating the need for an assignment phase)
The ctor can have multiple reloads.
Ctor in private zone
Singleton Mode
Class
{
Public A & getInstance ();
Setup (){...};
Private:
A ();
A (const A & rhs );
...
};
A & A: getInstance ()
{
Static A;
Return;
}
A: getInstance (). setup ();
Instances of objects cannot be directly created from outside, but can only be created through functions in the class.
Const member function (constant member function)
Double real () const {return re ;}
The const keyword declaration here will certainly not change the re_variable of the member in the class.
For example, if you do not write the preceding const, the following code blocks will be compiled incorrectly.
{
Const complex c1 (1, 2)
Cout <c1.real () <c1.imag ();
}
Because the user created a const object c1, which requires c1 to be a constant, and the member variables in it will also be const, which may not be changed; while calling the member function real () it is not declared as a constant member function, that is, the member variable re in the class will be "possibly" changed, which is contradictory
Parameter transfer: pass by value VS. pass by reference (to const)
Pass the original copy parameter by value, and pass the address that is equivalent to passing the parameter by reference (the underlying layer is a pointer); therefore, it is generally more efficient to pass the reference.
Example: complex & operator + = (const complex &);
The purpose is to pass in a complex object (reference) by reference, and declare that the passed object itself cannot be modified by function, [pass by reference (to const)]
Try to PASS Parameters by reference
Return value Transfer: pass by value VS. pass by reference
Under what circumstances can I pass by reference (to const )?
Under what circumstances can I return by reference?
For example, in the "_ doapl" function, its first parameter will be changed, and the second parameter will not be changed.
In this case, the second parameter can pass by reference (to const), and the first parameter can return by reference
The following is a correct example of return by reference:
Inline complex & _ doapl (complex * ths, const complex & r)
{
Ths-> re + = r. re;
Ths-> im + = r. im;
Return * ths;
}
Inline complex & complex: operator + = (const complex & r)
{
Return _ doapl (this, r );
}
Note that the first parameter is a pointer, pointing to an object that is not a local variable temporarily created in the function body, but a variable already exists outside the function, so you can return by reference
Under what circumstances cannot return by reference?
The local variable in the function body cannot return by reference. When the function ends, the local variable also disappears. If the content indicated by an "Address" from return by reference is corrupted
For example:
Inline complex & _ doapl (complex * ths, const complex & r)
{
Return (ths-> re + r. re );
}
Friend)
The private member re and im in the class cannot be directly used by the outside world (you can use the class functions), but the Friends function in the class can directly obtain the private member of its "friend ".
Class complex
{
Private:
Double re, im;
Friend complex & _ doapl (complex *, const complex &);
};
Inline complex & _ doapl (complex * ths, const complex & r)
{
Ths-> re + = r. re; // freely obtain the private member of friend
Ths-> im + = r. im;
}
Objects of the same class are mutual friends)
Class complex
{
Public:
Int func (const complex & param)
{Return param. re + param. im ;}
// Private Members of "friends" are directly used here
Private:
Double re, im;
};
{
Complex c1 (2, 1 );
Complex c2;
// C1 and c2 are mutual friends
C2.func (c1 );
}
Compile a small summary of a class
Data must be put in private
PASS Parameters and return values as much as possible by reference
In the class body member function, const must be added (constant member function)
Ctor tries its best to use its initial value column Mechanism
Big Three Special Functions
Copy construction, copy assignment, destructor
Make sure to check whether self assignment is in the copy assignment.
Inline String & String: operator = (const String & str)
{
If (this = & str)
Return * this;
//...
}
Memory Management
New: first allocates memory and then calls ctor
Complex * pc = new Complex (1, 2 );
Compiler conversion:
Complex * pc;
Void * mem = operator new (sizeof (Complex); // allocate memory
Pc = static_cast <Complex *> (mem); // force type conversion
Pc-> Complex: Compelex (1, 2); // call the constructor
// Complex: Compelex (pc is equivalent to hiding this pointer, 1, 2 );
Delete: Call the dtor before releasing the memory.
Delete pc;
Compiler conversion:
Complex ::~ Complex (pc); // destructor
Operator delete (pc); // release memory
Note: "operator new ()" and "operator delete ()" Are special C ++ system functions. The former is used to allocate memory and internally calls malloc (); the latter releases the memory and calls free () internally ()
Static data members and static member functions
The static data in the class only exists in one copy (in the "Global/static storage zone ")
Static functions do not have this pointer, so static functions can only process static data in the global/static storage area, but cannot access non-static data members in the class.
For example, designing a bank account
Class Account
{
Public:
Static double m_rate; // Interest Rate
Static void set_rate (const double & x)
{M_rate = x ;}
};
Double Account: m_rate = 0.01; // The Assignment Method of static data members 1 (static data member initialization)
Int main ()
{
Account: set_rate (0.02); // value assignment method 2 for static data members (call static functions through class name)
Account a; // Assignment Method of static data members 3 (call static functions through objects)
A. set_rate (0.03 );
}
Class template
Example:
Template <typename T>
Class complex
{
Public:
Complex (T r = 0, T I = 0)
: Re (r), im (I)
{}
Private:
T re, im;
};
{
Complex <double> c1 (2.0, 1.0); // Replace "T" in the class with "double"
Complex <int> c2 (2, 1); // Replace "T" in the class with "int"
}
Function template
For example, there is a stone class:
Class
{
Public:
Stone (int w, int h, int we)
: Width (w), height (h), weight (we)
{}
Bool operator <(const stone & rhs) const
{Return weight <rhs. weight ;}
Private:
Int width, height, weight;
};
To create two stone objects and compare stone weights:
Stone r1 (1, 2, 3), r2 (2, 3, 4 );
R3 = min (r1, r2 );
If function templates are available:
Template <class T>
Inline const T & min (const T & a, const T & B)
{
Return B <? B:;
}
The "T" in the function template is replaced with the class name "stone"
Because r1 and r2 are both stone classes, the compiler will deduce the argument deduction parameter of the function template.
When we compare the object size, because "T" is "stone", we call stone: operator <
Combination and inheritance
Composition (composite) indicates "has-"
Structure and analysis of composite relationships
Construct from the inside out
The Container constructor first calls the default constructor of Component, and then runs
Analysis structure from external to internal
The iner destructor first executes itself, and then calls the Component destructor.
Delegation (delegate) or Composition by reference
Bridging Mode (Handle/Body mode) or pImpl (Pointer to Implementation)
There is a String class. In addition to containing the necessary declaration of the class, the actual implementation method is defined in the StringRep of another class. In the String class, set a pointer to the StringRep class of the specific implementation.
// File String. hpp
Class StringRep; // Declaration
Class String
{
Public:
String (); // default Structure
String (const char * s); // copy Construction
String (const String & s); // copy Construction
String & operator = (const String & s); // copy value assignment
~ String (); // destructor
Private:
StringRep * rep; // Handle/body (pImpl)
}
// File String. cpp
# Include "String. hpp"
Namespace {
Class StringRep {
Friend class String;
StringRep (const char * s );
~ StringRep ();
Int count;
Char * rep;
};
}
Inheritance (Inheritance) indicates "is-"
Construct from the inside out
The constructor of Derived (Derived class/subclass) first calls the default constructor of Base (Base class/parent class), and then runs
Analysis structure from external to internal
The Derived destructor executes itself first, and then calls
The dtor of base class must be virtual; otherwise, undefined behavior will appear.
Virtual functions and Polymorphism
Inheritance (inherited) with virtual functions (virtual function)
Non-virtual function: you do not want the derived class to be redefined (override/Rewrite) it;
Virtual function: You want to redefine the derived class (override/Rewrite), and you have default definitions for it;
Pure virtual function: You want to redefine the derived class (override/Rewrite). You have no default definition for it.
Conversion function
Class Fraction
{
Public:
Fraction (int num, int den = 1)
: M_numerator (num), m_denominator (den ){}
Operator double () const
{
Return (double) (m_numberator * 1.0/m_denominator );
}
Private:
Int m_numerator; // molecule
Int m_denominator; // denominator
};
When executing the following code block:
{
Fraction f (3, 5 );
Double d = 4 + f;
}
First construct a Fraction 3/5, then try to add double and Fraction, and get a double value
The compiler first checks whether the operator + of double + Fraction is defined. If the operator + function is called, the compiler checks whether the class Fraction defines the Conversion Function of Fraction to double, if yes, call the conversion function. Otherwise, the compilation is incorrect.
Non-explicit-one-argument ctor
Class Fraction
{
Public:
Fraction (int num, int den = 1)
: M_numerator (num), m_denominator (den ){}
Fraction operator + (const Fraction & f)
{
Return Fraction (...);
}
Private:
Int m_numerator; // molecule
Int m_denominator; // denominator
};
When executing the following code block:
{
Fraction f (3, 5 );
Fraction d2 = f + 4;
}
The left operand is Fraction, the right operand is double, the operator + function's left operand is Fraction (implicit this pointer), and the right operand is also Fraction; therefore, the compiler will try to call non-explicit ctor to convert "4" to "Fraction ()" and then call operator +
Conversion function and non-explicit-one-argument ctor coexist
Class Fraction
{
Public:
Fraction (int num, int den = 1)
: M_numerator (num), m_denominator (den ){}
// Conversion function
Operator double () const
{Return (double) (m_numerator * 1.0/m_denominator );}
// Non-explicit-one-argument ctor
Fraction operator + (const Fraction & f)
{Return Fraction (...);}
Private:
Int m_numerator; // molecule
Int m_denominator; // denominator
};
When executing the following code block:
{
Fraction f (3, 5 );
Fraction d2 = f + 4;
}
Either of the two can be called, which produces ambiguity and compilation errors.
Explicit-one-argument ctor
Class Fraction
{
Public:
Explicit Fraction (int num, int den = 1)
: M_numerator (num), m_denominator (den ){}
Fraction operator + (const Fraction & f)
{Return Fraction (...);}
Private:
Int m_numerator; // molecule
Int m_denominator; // denominator
};
When executing the following code block:
{
Fraction f (3, 5 );
Fraction d2 = f + 4; // [error] To "Fraction d2 = f + Fraction (4);" you can use
}
An error will be reported, because under the limit of the "explicit" keyword, the real parameter of the operator + function must be of the Fraction type, and Fraction can be constructed even if only one int parameter is required (for example, Fraction (4) that is, 4/1), but the explicit it still does not allow this behavior
Pointer-like class about smart pointers
Smart pointers encapsulate a general pointer into a class and reload * and->, so that smart pointers can not only perform general pointer operations, but also expand other operations in the class.
General framework of smart pointers:
Template <class T>
Class shared_ptr
{
Public:
T & operator * () const
{Return * px ;}
T * operator-> () const
{Return px ;}
Shared_ptr (T * p): px (p) {}// constructor typically accepts a "real Pointer" to construct this "smart pointer"
Private:
T * px; // pointer to class T Type
//...
};
It is equivalent to a "smart pointer" shared_ptr (actually a class), which contains a "real Pointer" px (private variable; a pointer pointing to class T type)
Example:
Struct Foo
{
//...
Void method (void ){...}
};
Shared_ptr <Foo> sp (new Foo );
Foo f (* sp );
Sp-> method ();
First, declare a smart pointer sp pointing to class Foo;
Then, * sp will return the reference of * px (class Foo object) to create the class Foo object f through the copy constructor of class Foo.
Finally, sp-> will return px (a general pointer pointing to the class Foo type), so sp-> method () is equivalent to px-> method ()
Pointer-like class about iterator
Struct _ list_node // linked list element
{
Void * prev;
Void * next;
T data; // here T is assumed to be struct Foo
}
Struct _ list_iterator // linked list iterator
{
//...
Typedef _ list_node <T> * link_type;
Link_type node; // pointer to _ list_node object
// Typedef T & reference
Reference operator * () const
{
Return (* node). data;
}
// Typedef T * pointer
Pointer operator-> () const
{
Return & (operator * (); // calls the operator * function in the class and returns a reference of the T object)
// Then use & get address to return a pointer of the T * type.
}
// In addition, The iterator not only needs to process * and->, but also needs to process ++ -- operations. This is not extended here...
};
Example:
List <Foo >:: iterator ite;
Ite-> method ();
First, apply for an iterator ite, which is a linked list with the element type of class Foo.
Then, there is an operation: ite-> method (), which calls the member function method () of class Foo through the iterator ite ()
It means calling Foo: method ();
Equivalent to (* ite). method (); Because * ite will get a Foo object;
Equivalent to (& (* ite)-> method (); Because & (* ite) will get the pointer to Foo object, and then call method () through this pointer ();
Because "ite-> method ()" is eventually equivalent to "(& (* ite)-> method ()", in response to this operation requirement, this is why operator> function writing in struct _ list_iterator ["& (operator * ()" response "& (* ite)"]
Partial specialization, template specific-number biased
Template <typename T, typename Alloc =...>
(1)
Class vector
{
...
};
Bind the first template parameter to the bool type:
(2)
Class vector <bool, Alloc =...>
{
...
};
In this way, when the first parameter of the template is limited to the bool type, the code segment (2) instead of (1) will be used)
Note: Only one or more parameters can be bound from the first template parameter.
Partial specialization, template-partial range
Template <typename T>
(1)
Class C
{
...
};
When the template parameter range is limited to a pointer to the T type:
(2)
Class C <T *>
{
...
};
In this way, class C will be used when class C is created as T type (1), and class C will be created as T * type (2)
For example:
C <string> obj1; // use (1) to create obj1
C <string *> obj2; // use (2) to create obj2
Comparison Between reference and pointer
The pointer can be declared without initialization, but the reference must be initialized during the declaration, and after the reference is initialized, it cannot be assigned a value.
For example:
Int x = 0;
Int * px = & x; // OK
Int * px;
Px = & x; // OK
Int & rx = x; // OK
Int & rx; // no; must be initialized during Declaration
Int y;
Rx = y; // no; it cannot be assigned a value after initialization.
The object and its reference are of the same size and address:
Sizeof (rx) = sizeof (x)
& Rx = & r
(Although the compiler will tell you this, it is an illusion)
Reference is usually not used to declare variables, but is usually used to describe the parameter type (parameters type) and return type (return type ).
The following is considered as "same signature", and the two cannot both exist:
Double imag (const double & im )...{...}
Double imag (const double im )...{...}
For example:
Double d = 1.0;
Imag (d); // ambiguity
If the above two functions coexist, imag (d) will produce ambiguity. Because both functions can be called, the compiler cannot decide which one to choose.
Extension:
Q: Is const part of the function signature?
A: Yes.
For example:
Int f (...) const {...}
Int f (...){...}
They are considered to be two different functions (different function signatures)