C + + learning notes
quote 📖
1. Definition of reference
int a = 3; int &r = a; // In this case, it is called r reference a, and r is the alias of A
Note: after initializing the reference, it cannot be modified
int b = 4; r = b; //At this time, it is only equivalent to assigning the value of b to a;
2. Function of reference
- When r refers to a, the modification of r will also take effect for a
r = 7; cout << a << endl; cout << t << endl; /* Output: 7 7 */
- References can also refer to "references"
int &r_to_r = r; // Right r_to_r modification also has the same effect, and r refers to a at the same time
3. Frequently cited
- The referenced value cannot be modified by constant reference
int a = 3; const int &r = a; r = 7; //This will not work. There is a compilation error a = 7; //You can modify itself
-
The relationship between frequent reference and unusual reference
- Const ElemType & and ElemType & are different types
- ElemType & or ElemType variables can be used to initialize const ElemType&
int a = 3; int &r = a; const int & r_static = r;
- But const ElemType & cannot be used to initialize ElemType&
int a = 3; const int &r_static = a; int &r = r_static; // Compilation error // Because r_static is a constant reference (reference value cannot be modified), and cannot be modified through another reference
4. Practical application
- Modify with pointer
void swap(int* a, int* b){ int temp = *a; *a = *b; *b = temp; } swap(&a, &b);
- Modify with reference
void swap(int &a, int &b){ int temp = a; a = b; b = temp; } swap(a, b);
Such a contrast, is it a lot simpler to quote!
5. Strange return value
int& getElement(int * a, int i) { return a[i]; // Defines that the return value is a reference type } int main() { int a[] = {1,2,3}; getElement(a,1) = 10; // Modify the reference of the returned a[1] to 10 cout << a[1] ; // Output 10 return 0; }
Dynamic memory allocation 🔥
1. Use of new
In C language, we use malloc function to dynamically allocate memory, while in C + +, we use new operator to complete this operation
- Request a variable memory of type ElemType
int *p; p = new int; // At this time, p points to the memory space with the size of sizeof(int) *p = 4; //Attempt assignment cout << p << endl; //The output result is: 4
- Use new to apply for a contiguous memory space (array)
int *p_to_arr; int n = 5; p_to_arr = new int[n*20] //Can be an integer expression *p_to_arr = 4; // p_to_arr is the pointer to the first address of the array cout << p_to_arr[0] << endl; // Output: 4
The return results of both new are of type ElemType *
2. Use of delete
When the dynamically allocated memory is not needed, we can use delete to free the memory
- Free up single variable space
delete p;
- Free up a contiguous memory space
delete[] p_to_arr; delete p_to_arr; // This is wrong and only frees up a single space
delete cannot be used to free memory space that is not dynamically allocated
Inline function ⚔️
1. Inline function
When the functions of some functions are relatively simple and need to be executed many times, it can avoid the related operation of putting functions on and off the stack. Using inline functions, the whole function code is inserted into the call statement to improve the efficiency of the program
- definition
inline int Max(int a, int b){ return a > b ? a : b; //Returns the maximum value }
- Actual performance
k = Max(3, 4); // Equivalent to the following code snippet tmp = 3 > 4 ? 3 : 4; // Store the value of return as a variable k = tmp;
The disadvantage of inline functions is that it will increase the code memory of the main function
2. Function overloading
The operation of defining one or more functions with the same name but inconsistent parameter type, return value type or number is called function overloading
For example:
(1) int Max(double f1, double f2){}
(2) int Max(int n1, int n2){}
(3) int Max(int n1, int n2, int n3){}
Max(3.2, 1.2); //Call (1) Max(1, 2); //Call (2) Max(1, 4, 2); //Call (3) Max(1, 2.2); //Unable to distinguish, compilation error // The following can not be distinguished, because there can be no parameters int max(int x = 0){} void max(){}
advantage:
- Make the naming of functions simple
- It is easy to call a function. The compiler judges which Max is based on the type and number of parameters
3. Default parameters of function
Similar to the function default value of Python, if you assign a value to a function parameter when defining a function, the parameter has a default value and can be omitted when calling
void func(int x1, int x2 = 2, int x3 = 3){}
func(1); //accord with func(2); //accord with func(2,,3) //Compilation error. The compiler cannot tell which parameter is saved
advantage
- It improves the expansibility of the program. If you want to add features to a function, you only need to give one more parameter, and other places can remain unchanged
Classes and objects
The concept of object-oriented is generally used in large-scale project development, because classes summarize the general characteristics of something, including clear relationship between member variables and member functions, which is easy to understand, modify, check and reuse code
The idea of structured design: simply encapsulating some functions is conducive to multiple calls. The relationship between various variables and functions in the code is unclear. When the project scale is too large, it is difficult to read and maintain. This is why the mainstream idea is object-oriented programming
1. C lass definition in C + +
class CRectangle; // You can declare a class before you define it class CRectangle{ public: // int h, w; void set_val(int a, int b){ w = a; w = b; } int calArea(){ return w*h; } int calPeri(){ return 2*(w+h); } }; // Remember to add a semicolon
Another way: define functions outside the class
class CRectangle{ public: int h, w; void set_val(int, int); int calArea(); int calPeri(); }; void CRectangle::set_val(int a, int b){} int CRectangle::calArea(){} int CRectangle::calPeri(){}
2. Usage
- Instantiate object
CRectangle r; // Used like a type, similar to a structure
- Access member
r.set_val(3, 4); // Set the width and height to 3 and 4 respectively
cout << r.w << endl; // Output 3
3. Pointer, reference
// Pointer CRectangle r; CRectangle* pr = &r; p->set_val(3, 4); p->w; // quote CRectangle &rr = r; rr.set_val(3, 4); rr.w;
These are the basic operations of classes in c + +
4. Modifier
- Private: private attribute, which can only be accessed through internal methods
- Public: public attribute, which can be accessed directly
- Protected: protected attribute, unknown at present
The advantage of private property is that it avoids random access to the variable. When you want to modify it, you need to change it all. If it is private, you can only modify it through internal functions, just modify internal functions
// Public szName strcpy(Person.szName, "Tom123456789") // Private Person person; person.setName("Tom123456789") // Suppose we modify the internal property szName with a length of 5, Tom123 Obviously cross the border /* 1. For public, we need to change the statements one by one 2. For private, we only need to add a judgment to the internal function */
-
Function of member
- You can access all the properties and functions of the current object
- You can also access all properties and functions of other objects of the same kind
int add(A obj1, A obj2){ return obj1.x + obj2.x // Can be private }
5. Constructor
The constructor will be called automatically when instantiating an object. It is generally used to initialize some attribute variables of the object and Python__ init__ The method is similar, which is conducive to the operation of the program
Note: the constructor does not allocate memory space. The allocation is already done when the object is generated. The constructor is just initialization!
- The constructor name should be the same as the class name and can have parameters
- The constructor does not need to return a value, and does not need to write the return value type (void is not allowed)
class Test{ Test(int a, int b); // The constructor Test is declared here and can be defined externally }
-
A function can have multiple constructors (not overloaded) = = > type conversion constructors
-
This function is very similar to the constructor and can be considered as a special constructor
-
For example:
-
Test a = 2; This is just a call to the constructor
-
class Test{ public: int val; Test(){} //Constructor is empty by default Test(int a){ val = a; } } int main(){ Test a; a = 2; //The type conversion constructor is called here a.getVal(); return 0; }
-
🔥 The essence of the type conversion constructor is to convert the values of other types on the right of the equal sign into a temporary object, assign the value to the object on the left of the equal sign, and then die
-
-
-
If no constructor is written, the compiler generates a parameterless constructor by default
-
Constructor supports overloading
Test::Test(){} Test::Test(int a){} Test::Test(int a, int b){}
Several situations in which the constructor is called (assuming that the constructor has parameters)
-
Instance object
-
Test a(1); correct
-
Test a; Error, no parameters added
-
Test a = 2; correct
-
Test a; 🔥
a = 2; Error, a = 2 is not an initialization statement
-
-
Defines a pointer to a class
- Test* p = new Test(1); correct
- Test* p = new Test; error
- Test* p = new Test[2]; Error, but unknown solution
- object array
- Test array[2] = {1, 2}; correct
- Test array[2] = {Test(1), Test(2)} correct
- Test array[2]; Error, no parameters
- Class pointer array
- Test *p[2] = {new Test(1), NULL} is correct because it is a pointer, not an object
- Test *p[2] = {new Test, NULL} error, no parameter
6. Copy constructor
definition:
- The copy constructor is similar to the constructor, but the former has only one parameter and is the reference object of this class
- For example, if you choose between test:: Test (Test & x) or test:: Test (const test & x), you can only define one copy constructor, which can reference constants
- If there is no copy constructor, the compiler generates by default
class Test{ public: int val; Test(int a){ // Constructor val = a; } Test(Test& a){ // Complex function val = a.val; cout << "Called" << endl; } };
So why can't the copy constructor be written as test:: Test (test X)?
Because this involves the relationship between the parameter passing mechanism of the function and the copy constructor If this method is adopted, when calling the copy constructor, first pass the parameter, and then call the copy constructor again. Repeat = = > dead recursion
Several situations in which the copy constructor is called
- When an object is initialized with the value of another object of the same kind
Test a1(2); Test a2(a1); // The complex function is called // Equivalent to Test a2 = a1; It's initialization, not assignment statement!
Error prone:
Test a = 2; Test b = 1; b = a; // The copy constructor is not called when an object is assigned a value by another object
- When an object is passed as a parameter of a function
void func(Test a){} Test a(2); func(a); // The complex function is called
Sometimes, in order to speed up the running speed of the program, the parameter is set to test & A, so there is no need to pass the parameter, and const can also be added
- When an object is used as the return value of a function, a temporary object func() is generated
Test func(int a){ Test x(a); return x; } cout << func(3).val << endl; /*Output: Called 3 */
Note: in dev C + +, the function return value does not call the copy constructor, but returns directly
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-d2lzicec-164354309015) (C: \ users \ a \ appdata \ roaming \ typora \ typora user images \ image-20220116194614953. PNG)]
7. Destructor
definition:
- When an object dies, the destructor is called
- The function name is the same as the class name. There are no parameters and return values. Add a ~ before the name to represent the destructor
class Test{ public: ~Test(){ printf("destructor called!\n"); } }
Note: the destructor does not destroy memory, but does something before destroying it
Several cases of object extinction
- The program ends and all variables are destroyed
- As the end of a local variable
- The end of the object generated by the calling function
Specific examples
Test func(Test lobj){ return lobj; // 1. Extinction of local variable lobj } int main(){ Test obj; obj = func(obj); // 2. The function call ends and the generated object dies return 0; // 3. The demise of obj at the end of the program } /*Output result: destructor destructor destructor */
Special case: when a memory is dynamically requested but not destroyed, the program will not die when it ends
8. this pointer
this pointer is equivalent to a pointer to the current object. What's the use?
The concept of class and object in C + + can actually be translated into the structure of C language. It can be considered that C + + code is translated into C language first and then compiled
class Test{ // C++ public: int val; void setVal(int e){ val = e; } };
struct Test{ // C language int val; }; // The focus is on the translation of member functions, which are "bound" by using pointers to this type void setVal(struct Test* this, int e){ this->val = e; }
Practical application: in non static member functions, this can be directly used to represent the object of its action
class Complex{ public: int real, imag; void Print(){ cout << real << "," << imag; } // List? Complex(double r, double i):real(r), imag(i){} Complex AddOne(){ this->real++; // Equivalent to real + +, why do you want this??? this->Print(); // Equivalent Print() return *this; // Dereference returns the object it acts on } }; int main(){ Complex c1(1, 1), c2(0, 0); c2 = c1.AddOne(); return 0; }
Error prone area:
class Test{ int i; public: void Hello(){ cout << "hello" << endl;} };
Test* p = NULL; // Null pointer of type test
Try to judge whether p - > print() is true or false?
- Correct, it can be understood as a class object
Translated into C language: void hello (test * P) {cout < < hello "< < enld;}
The pointer is passed in. Although it is empty, it does not use any members of the object, so it is not really understood correctly
- If it is changed to void hello() {cout < < I < < endl;} Wrong, p does not point to any object, just a null pointer of this type
For static member functions, this pointer cannot be used because static member functions do not act on an object
9. Static members
A static member, which is not linked to any object, is essentially a global variable / function
Difference from ordinary members:
- The common member variables of each object are different, but they share static member variables
- A normal member function must act on an object, and a static member function is not related to it
- Static member = > printh:: can be accessed directly; 👇
Advantages: the purpose is to write some global variables / functions related to some classes into the class definition, which is conducive to code maintenance and understanding
be careful:
- After the static member variable is defined, it should be defined and initialized again in the global scope to connect the variable with the class
class Test { public: static int real, imag; static void printH() { cout << "Hello World" << endl; } Test() {} }; int Test::real = 0; int Test::imag = 0;
- After that, you can directly use Test::real to access static member variables. Private variables cannot be accessed directly
- Static member functions cannot access non static member variables because they do not bind any objects and cannot determine whose members they are
- You can't call non static member functions. What if non static member variables are accessed in this function 😃
Application examples:
class CRectangle{ private: int w, h; static int nTotalArea; // Total records static int nTotalNumber; // Record total area public: CRectangle(int w_, int h_); ~CRectangle(); static void PrintTotal(); // Used to print quantity and total area }; CRectangle::CRectangle(int w_, int h_){ w = w_; h = h_; nTotalNumber++; nTotalArea += w * h; } CRectangle::~CRectangle(){ // There are defects here nTotalNumber--; nTotalArea -= w * h; } void CRectangle::PrintTotal(){ cout << nTotalNumber << ',' << nTotalArea << endl; } // Initialization of static member variables, private can also be initialized directly int CRectangle::nTotalNumber = 0; int CRectangle::nTotalArea = 0; int main(){ CRectangle r1(3, 3), r2(2, 2); // cout << CRectangle::nTotalNumber << endl; <= Wrong, private members cannot be accessed directly CRectangle::PrintTotal(); r1.PrintTotal(); return 0; }
Defects:
When the calling parameter is this class or the return value is this class, the copy constructor will generate a temporary object. At this time, it will not be regarded as a constructor to add an object, but it will be subtracted during deconstruction
Method:
Rewrite the complex function to increase the number when it is copied (some objects are copied), which can be offset when it is destructed
10. Member objects and closed classes
Definition: a class with member objects is called a closed class
Generally speaking: when defining a class, if one member is an object of another class, the class is called a closed class
Example explanation:
class Tyre{ private: int radius; int width; public: Tyre(int r, int w):radius(r), width(w) {} // Initialization list }; class Engine{ }; class Car{ // Members will include tires, engines, and closed classes private: int price; Tyre tyre; // This is called declaration. There is no initialization, and there is no initialization here Engine engine; public: // Use the constructor of the closed class to initialize the member object Car(int p, int tr, int w):price(p), tyre(tr, w) {} }; int main(){ Car car(20000, 17, 225); return 0; }
Note: if Car does not define a constructor, the compiler will report an error when calling the default constructor, because the member object needs to be initialized
Any statement that generates a closed class object should use a defined constructor to initialize the declared member object that needs to be initialized
Initialization list: the parameters in the list can be any meaningful parameters, any complex expression or function
Execution order of constructor and destructor of closed class:
- Before constructing closed class objects, construct member class objects from the inside layer by layer
- Internal member objects are constructed in the order of declaration
- For destructors, construct first and then destruct according to the order of stacks
Copy constructor of closed class:
The default complex function of a closed class is to call the complex function of the class to which the member object in the closed class belongs
Because if the object of a closed class is generated by copying, the member object in it has not been initialized yet. You must call the complex function of the class to which the member object belongs, or use the initialization list to initialize
give an example:
// Assuming that there is a member object of class A with class B, the complex function of class A should be written as follows A(const A& obj):obj_B(obj.obj_B) {} // Instead of writing like this A(const A& obj){ obj_B = obj.obj_B; // He will think that you did not initialize, but directly assign values }
11. Constant object, constant member function
Constants are immutable quantities, and most provisions are similar to static members
Example:
- Define constant object - > const test obj;
- Define constant member function - > void func() const {}
- Constant member functions cannot modify member variables or call member functions. What if they are modified in the function
- However, you can modify the value of static variables and call static member functions
Correlation between the two: constant objects can call constant member functions, not non constant member functions
Note: if two member functions have the same name and parameter table, but one has const, the two functions are overloaded
12. Friends
Friend means friend, which is declared with the keyword friend
Friends are divided into friend functions and friend classes
Friend function: it can be a member function of a class (including constructor and destructor) or a global function
- The friend function of a class can access the private members of the class
class CCar { private: int price; // Public: friends are not public, they are separate friend int MostExpensiveCar(CCar cars[], int total); friend void CDriver::ModifyCar(CCar* pCar) } void CDriver::ModifyCar(CCar* pCar){ cout << pCar->price << endl; // Private members can be accessed directly }
- If the friend class of class B is class A, the member function of class a can access the private members of the class
class CCar{ private: int price; friend class CDriver; // Declare as friend class }
The relationship between friends is not mutual and cannot be passed on or inherited
Operator overloading
Ordinary operators are used to operate General data types and cannot act on the operation of objects. Sometimes the operation of objects needs to be carried out. At this time, operators need to be overloaded so that objects can operate
Form of operator overload:
- The essence of operator overloading is function overloading
- It can be overloaded as a normal function or a member function
- In the compilation process, the expression containing the operator is converted into a call to the operator function
Operator overloaded functions can also be overloaded. The number of overloaded functions depends on the parameter type
1. Overloading of arithmetic operators
Overload declaration form:
Return value type operator operator symbol (parameter table) {}
Use operator as the special function name, followed by the declaration of overloaded symbols
- Overloaded functions are ordinary functions with two parameters
Complex operator+( const Complex& a, const Complex& b){ return Complex(a.val+b.val); // Returns a new object formed by adding two objects }
- Overload is a member function, and the parameters are one item
Complex Complex::operator-( const Complex& obj){ return Complex(val-obj.val); }
a + b; // Equivalent to operator+(a, b); a - b; // Equivalent to a calling the member function operator-(b);
In fact, ordinary functions and member functions are different!!!
Examples are as follows:
// If only member functions are defined Complex Complex::operator + ( double r ){ return Complex(r + val); } // The expression c = c + 5 is OK, and 5 is used as the parameter of the member function // The expression c = 5 + c is wrong. Here you need to use an ordinary function to pass two parameters
2. Overload of assignment operator
The overload declaration form is consistent with the ordinary operator, but it can only be overloaded as a member function
When an object is assigned by a common data type or by an object, sometimes the meaning of the assignment operator needs to be rewritten
The following is an example of defining a String class:
class String { private: char* str; public: String():str(new char[1]) { str[0] = 0; } // Simple initialization const char* c_str() { return str; } // Easy to print member values // When assigned by constant string String& operator = (const char* s); ~String() { delete[] str; } };
- When assigned by constant string
String& String::operator= (const char* s){ delete[] str; // Release the original value str = new char[strlen(s)+1]; // New a new space to store strcpy( str, s ); return *this; // Return to itself }
In fact, after copying, our goal has been achieved. So why return ourselves? The type is still string&
- Assigned by similar objects
There is generally no problem with direct assignment between objects, but for this String class, there is no problem
The problem is: A = B;
The member of B used for assignment is a space from new. For ordinary assignment, the pointer will point to the same space
Disadvantages:
- When A / B is modified, the other will also be modified
- When A / B is reassigned by a constant string, the space of another is destroyed
- When A dies, the space has been released and B cannot be used, and when B dies, it will release the space again, resulting in an error
Finally, we need to customize the assignment operator between similar objects
String& operator= (const String& s){ delete[] str; str = new char[strlen(s.str)+1]; // Member functions can access private members strcpy(str, s.str); return *this; // Here we go back to ourselves }
Return itself, and the type is string & because:
- When a = b = c; It is required to assign the return value of b = c to a, so it needs to return itself
- When (a = b) = c; c is required to be assigned to the return value of a = B, so the return value type is string&
When overloading operators, it is a good habit to keep the original characteristics of operators
There are actually two problems with the above definition of String class
- Self assigned to itself, s = s; The space will be released first and then accessed. The changes are as follows
String& operator= (const String& s){ if( p == s.p ){ // If you are yourself, return directly return *this; } delete[] str; str = new char[strlen(s.str)+1]; // Member functions can access private members strcpy(str, s.str); return *this; // Here we go back to ourselves }
- When calling the String class copy constructor, the same space will also be shared. The changes are as follows:
String(const String& s){ // delete[] str; When the structure is initialized, you don't have to delete it? str = new char[strlen(s) + 1]; strcpy(str, strlen) }
3. Overload as friend function
Sometimes it is not enough to define member functions and ordinary functions
Defining member functions cannot use the expression of number + object, and ordinary functions cannot access the private members of the object, so they need to be overloaded as friend functions
friend Complex operator+(double r, const Complex& c);
4. Several knowledge points of realizing variable length array class
- The default parameter cannot be used as an initialization list parameter, but it can be declared and then take effect
CArray(int s = 0); // s represents the number of array elements CArray::CArray(int s):size(s) { //The statement is useful??? if(s == 0){ ptr = NULL; } else{ ptr = new int[s]; } }
- Overloading of bracket operator []
int& CArray::operator[] (int i){ return ptr[i]; }
- push_ Implementation of back
void CArray::push_back(int val) { if(size >= maxSize){ maxSize += 32; int* tmpPtr = new int[maxSize]; memcpy(tmpPtr, ptr, sizeof(int) * size); delete[] ptr; ptr = tmpPtr; } else if(!ptr) { maxSize = 32; ptr = new int[maxSize]; } ptr[size++] = val; // Place the value at the end and add one to the length }
5. Overload of stream operator
Stream operators, that is, symbols used for input and input, usually match <, > > with cout and CIN, and overload them into stream insertion and stream extraction operators respectively
cout is the object of ostream class, and cin is the object of ostream class, which can be called directly
Take * * cout output 5 / "hello" * * as an example:
- Overloaded member function with ostream
void ostream::operator<< (int n) {}
As mentioned earlier, the overloading of operators should try to retain its original characteristics
Such as cout < < 5 < < hello "; How should we achieve it?
The answer is very simple, that is to return cout itself, that is, the return value is ostream &, and the superposition operation can be performed
ostream& ostream::operator<< (int n) {}
Cout < < 5 < < the essence of "hello" is = > cout operator<<(5). operator<<(“hello”);
- Overloaded as normal function
When we need to perform some input and output operations on the object, we certainly can't modify the member function of ostream class, so we need to overload it into a global function
Examples are as follows:
class CStudent { private: int age; public: CStudent(){age = 5;} // As can be seen from the following declaration, it is sometimes necessary to declare as a friend function to access its private members friend ostream& operator<<(ostream& o, CStudent& student); }; // Overload < < symbol ostream& operator<<(ostream& o, CStudent& student){ // The reason why we refer to ostream class objects here is to avoid recursion o << student.age; // This function is a friend, so you can access its private members return o; } int main(){ CStudent student; cout << student; return 0; // Output result: 5 }
6. Overload of type conversion operator
When the object needs to be converted to xx type, the type conversion overloaded function will be called automatically
Definition form:
operator double() {}
When defining the type conversion operator, you do not need to write the return value type, because you need to return the overloaded type, and you do not need the parameter, because the parameter is only itself
Call:
- (double)variable when using the cast operator
- double x = 2 + obj; During the operation with basic data type, the function will be automatically called to convert the object into the corresponding type and then add it
- int x = 2 + obj; Although the converter of int type is not overloaded at this time, the compiler will convert the overloaded double type to int type
Equivalent to x = 2 + class operator double()
7. Overloading of self increasing and self decreasing operators
Overloaded pre + + and post + + overloaded functions have the same symbols, but the return value types are different due to different parameters. The pre and post functions are respectively overloaded by adding one more parameter
- Overload the pre + +, and return the reference of the object
// As member function Test& operator++(); // As a global function Test& operator++(Test& obj);
- Overload post + +, return temporary object
// As member function Test operator++(int k); // One more useless parameter // As a global function Test operator++(Test& obj, int k);
When it is used as a global function, the reference of the object should be passed in so that its member value can be modified. If it is private, it needs to be declared as a friend function of the class
Full function definition:
- Front++
Test& Test::operator++(){ ++n; return *this; }
- Postposition++
Test Test::operator++(int k){ Test temp(*this); n++; return temp; // Return temporary object }
From the definition of pre and post, it can be seen that + + i will be much faster when only self increment is required
8. Precautions for operator overloading
- C + + does not allow defining new operators
- The meaning of overloaded operators should conform to daily habits. For example, + means addition, not subtraction
- Operator overloading does not change the priority of the operator
- The following operators cannot be overloaded (".), " .* ", " :: ", " ?: ", " sizeof ")
- When overloaded operator (), [], - > or assignment operator =, overloaded function must be declared as member function of class
Inheritance and composition
1. Class inheritance
When defining a new class B, if the class is similar to class A (meaning that B has all the characteristics of a), it can take a as the base class and B as the derived class of A. this relationship is called class inheritance
Usage form of inheritance: class derived class name: public base class name {};
2. Derived class
Basic features:
- Derived classes are obtained by modifying and extending the base class
- After a derived class is defined, it can be used independently
- Derived classes have all members (variables and functions) of the base class, whether private, protected or public
- Derived classes cannot access private members of base classes
Memory space
- The memory space is equal to the volume of the members of the base class object plus the members added by the derived class object
- The storage location of the base class object is before the new member variable of the derived class object
Available functions:
class base { public: string name; void setInfo(const string& name_); void printInfo(); }; class Derived: public base { public: int nAge; void setInfo(const string& name_, int nAge_){ base::setInfo(name_); nAge = nAge_; } void printInfo(){ base::printInfo(); // Using base classes cout << nAge << endl; } };
3. Two relationships between classes
- Inheritance = > If a class B is a derived class of class A, it should logically meet "a B object is also an a object"
- Composition (composition) = > If a member of a class is an object of another class (closed class), it is called a composition relationship
How to use inheritance and composition
- inherit
When a variety of things have some common characteristics and are different from each other, we should summarize their common characteristics to define a base class, and then derive the required different classes
Example:
If a CMan represents a man and now needs to define a CWoman class, and CMan and CWoman have many common features, should CWoman be derived from CMan
According to logic: is a woman a man?
Therefore, the common characteristics of the two classes should be abstracted as the base class to derive CMan and CWoman classes
- combination
Sometimes inheritance is not necessarily appropriate for any situation
When this class has the characteristics of class A and class B, it can be combined as members to form a new class
class CPoint { double x, y; friend class CCircle; }; /* Inheritance (wrong practice) */ class CCircle: public CPoint { double r; }; /* combination */ class CCircle { double r; CPoint center; // Unique existence as the center of a circle };
A circle is not a point!!!
- Definition of "puppy" and "owner" relationship classes
class CDog; class CMaster { CDog* dogs[10]; }; class CDog { CMaster* m; }
4. Coverage
When a member with the same name as a base class member is defined in a derived class, an override occurs
Access members:
- Members of derived classes are accessed directly by name
- Members of a base class can be accessed by using the base class declaration plus the scope symbol::
Generally speaking, member variables with the same name are not defined in derived classes, and member functions are often overwritten
5. Modifier
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-a6yku6aa-164354309018) (C: \ users \ a \ appdata \ roaming \ typora \ typora user images \ image-20220129172510397. PNG)]
6. Constructor of derived class
Because the generation of derived class objects includes the generation of base class objects, when defining the constructor of derived class objects, you need to initialize the member variables of base class objects
Generally, the base class constructor is called to initialize the members of the base class object through the initialization list
Code example:
class Bug { int legs; int color; public: Bug(int legs_, int color_){ legs = legs_; color = color_; } }; class FlyBug: public Bug { // Derived class int wings; public: // Use the following initialization list to construct derived class objects FlyBug(int legs_, int color_, int wings_):Bug::Bug(legs_, color_){ wings = wings_; } };
By calling the constructor defined by the base class itself, you can avoid the problem of being unable to access its private members
Construction, destructor, call order
This order is similar to closed classes
- Call the constructor of the base class first
- If there is a member object, call the constructor of the member object
- Finally, the constructor of the derived class is called.
The calling order of destructors, on the contrary, follows the order of stacks, first in and last out
Assignment rules 7. public
Operations that can be implemented by a derived class that inherits the public base class
- Derived class objects can be assigned to base class objects
b = d;
2. Derived class objects can initialize references to base class objects
base& br = d;
3. The address of the derived class object can be assigned to the pointer of the base class object
base* pb = &d;
- Members of derived classes that inherit from the base class can only be accessed through this pointer
- You can convert a base class pointer variable type to a derived class pointer variable type by forcing type conversion
If it is inherited through private / protected mode, it cannot be implemented
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-hsppzpb7-164354309019) (C: \ users \ a \ appdata \ roaming \ typora \ typora user images \ image-20220129194742876. PNG)]
8. Multiple inheritance
Class B is derived from Class A, class C is derived from class B, and class C is generated by multiple inheritance of class A
- Class A is the direct base class of class B
- Class A is the indirect base class of class C
- Class B is the direct base class of class C
When defining a derived class, you only need to list its direct base class, and the derived class will automatically inherit its indirect base class upward along the class hierarchy
🎆 About the construction, the execution order of the destructor, the same is true
Standard template library (STL)
1. string class
It is the encapsulation of string and its related operations, and the highlighted one is the key usage
-
Use the string class to include the header file < string >
-
Initialization method of string object
-
string s1;
-
string s1("hello");
-
string s1 = "hello";
-
string s1(8, ‘x’); // Initializes to an 8 x character string
-
Bad initialization method:
- string s1 = 'n';
- string s1('n');
- string s1(6);
- string s1 = 6;
-
Magic: you can assign characters to string objects
string s1; s1 = 'n';
-
-
Member function for getting the length of string object: length()
-
string supports stream reading
cin >> str;
- string supports getline function
getline(cin, s);
- Assignment with = sign
s2 = s1;
- Copy with assign member function
// Full replication s2.assign(s1); // Partial replication s2.assign(s1, 1, 3); // Three characters starting from the subscript 1 of s1 are copied to s2
- Single character copy
s2[5] = s1[3] = 'n';
- Use the at member function to access the value in the string object
string s1("hello"); for(int i = 0; i < s1.length(); i++) cout << s1.at(i) << endl;
The difference between at and brackets is that at will check the boundary and report out if it exceeds the limit_ of_ Range exception
- Connect string with + sign
string s1("good"), s2("morning"); s1 += s2; // good morning
- Connect string with member function append
// Full connection s1.append(s2); // Partial connection s1.append(s2, 0, s2.size()); // size characters from 0
If there are not enough characters, wait until the last character is connected
- You can use relational operators (<, < =, >, > =, = = and so on) to compare the dictionary order of string s
- Compare the dictionary order of string s with the member function compare
// Compare all s1.compare(s2); // Unilateral partial comparison s1.compare(1, 2, s2); // Starting from 1, the ratio of two and s2 // Two sided partial comparison s1.compare(1, 2, s2, 0, 2) // Use the ratio of s1 from 1 to 2, and s2 from 0 to 2
- Get substring of string with member function substr
s2 = s1.substr(4, 5); // Assign s1 5 characters from 4 to s2
- swap exchange content
s1.swap(s2)
- find finds content from a string
string s1("hello"); // Find from left to right s1.find("lo"); // Find from right to left s1.rfind("lo"); // Find from specified location s1.find("lo", 1); // Start with subscript 1 // Look for one of them from left to right s1.find_first_of("abcd"); // Look for one from right to left s1.find_last_of("abcd"); // Can't find one of them find_last_not_of, find_first_not_of
If found, the subscript that appears for the first time will be returned. If not, the static constant string::npos will be returned;
- erase deletes elements from a string
// Delete from specified location s1.erase(5) // Starting from 5
- replace replaces the element in the string
// Replace with all s1.replace(2, 3, "hh"); // Replace s1 with "hh" for 3 characters starting from 2 // Replace with part s1.replace(2, 3, "haha", 1, 2); // Replace with 2 from 1 in "haha"
- insert inserts an element into a string
string s1 = "hello"; string s2 = "ha"; // Insert all s1.insert(3, s2); // Partial insertion s1.insert(3, s2, 0, 1); // Insert 1 character from 0 in s2
- c_str returns const char * type string
- data returns char * type string
- The copy member function copies itself to other variables
string s1("hello world"); int len = s1.length(); char* p2 = new char[len+1]; s1.copy(p2, 5, 0); // Return to 5 p2[5] = 0; cout << p2 << endl;
Copy s1 with a maximum of 5 characters from 0 to p2, and return the actual number of copied characters
-
String stream processing
- Instantiate stream and extract objects with istringstream class
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-o6esnykm-164354309020) (C: \ users \ a \ appdata \ roaming \ typora user images \ image-20220124154138330. PNG)]
- Instantiate with ostringstream class
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-zmukvpcm-164354309021) (C: \ users \ a \ appdata \ roaming \ typora \ typora user images \ image-20220124154355414. PNG)]
If there are not enough characters, Until the last character is connected - You can use relational operators(<, <=, >, >=, ==etc.)compare string Dictionary order - Using member functions==compare==compare string Dictionary order ```cpp // Compare all s1.compare(s2); // Unilateral partial comparison s1.compare(1, 2, s2); // Starting from 1, the ratio of two and s2 // Two sided partial comparison s1.compare(1, 2, s2, 0, 2) // Use the ratio of s1 from 1 to 2, and s2 from 0 to 2
- Get substring of string with member function substr
s2 = s1.substr(4, 5); // Assign s1 5 characters from 4 to s2
- swap exchange content
s1.swap(s2)
- find finds content from a string
string s1("hello"); // Find from left to right s1.find("lo"); // Find from right to left s1.rfind("lo"); // Find from specified location s1.find("lo", 1); // Start with subscript 1 // Look for one of them from left to right s1.find_first_of("abcd"); // Look for one from right to left s1.find_last_of("abcd"); // Can't find one of them find_last_not_of, find_first_not_of
If found, the subscript that appears for the first time will be returned. If not, the static constant string::npos will be returned;
- erase deletes elements from a string
// Delete from specified location s1.erase(5) // Starting from 5
- replace replaces the element in the string
// Replace with all s1.replace(2, 3, "hh"); // Replace s1 with "hh" for 3 characters starting from 2 // Replace with part s1.replace(2, 3, "haha", 1, 2); // Replace with 2 from 1 in "haha"
- insert inserts an element into a string
string s1 = "hello"; string s2 = "ha"; // Insert all s1.insert(3, s2); // Partial insertion s1.insert(3, s2, 0, 1); // Insert 1 character from 0 in s2
- c_str returns const char * type string
- data returns char * type string
- The copy member function copies itself to other variables
string s1("hello world"); int len = s1.length(); char* p2 = new char[len+1]; s1.copy(p2, 5, 0); // Return to 5 p2[5] = 0; cout << p2 << endl;
Copy s1 with a maximum of 5 characters from 0 to p2, and return the actual number of copied characters
-
String stream processing
- Instantiate stream and extract objects with istringstream class
[external chain pictures are being transferred... (img-o6esnykm-164354309020)]
- Instantiate with ostringstream class
[external chain picture transferring... (img-zmukvpcm-164354309021)]