C + + learning notes

Posted by gufmn on Mon, 31 Jan 2022 22:10:15 +0100

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

    1. You can access all the properties and functions of the current object
    2. 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:

      1. Test a = 2; This is just a call to the constructor

      2. 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;
        }
        
      3. 🔥 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)

  1. 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

  2. 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
    1. object array
    • Test array[2] = {1, 2}; correct
    • Test array[2] = {Test(1), Test(2)} correct
    • Test array[2]; Error, no parameters
    1. 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

  1. 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
  1. 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

  1. 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

  1. The program ends and all variables are destroyed
  2. As the end of a local variable
  3. 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

  1. 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
}
  1. 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

  1. C + + does not allow defining new operators
  2. The meaning of overloaded operators should conform to daily habits. For example, + means addition, not subtraction
  3. Operator overloading does not change the priority of the operator
  4. The following operators cannot be overloaded (".), " .* ", " :: ", " ?: ", " sizeof ")
  5. 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

  1. Call the constructor of the base class first
  2. If there is a member object, call the constructor of the member object
  3. 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

  1. 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)]

Topics: C++ Back-end