C++ Review Notes: Classes and Objects

Posted by idweb on Sat, 25 Sep 2021 18:13:49 +0200

c++ online compilation tool for fast experimentation: https://www.dooccn.com/cpp/

1.Write before

During this period of time, I plan to pick up c++. One of the lessons I learned from my internship is that Algorithm Engineers solve practical problems, so they can't be limited by algorithms or projects. They should always improve their ability to solve problems. In this process, I found that cpp is very important.This is precisely the time when I have been exposed to some tasks related to c++ development, and all of you want to take this opportunity to learn c++ again. In the recommended areas, the algorithm models I have come across are mainly based on Python, while the online services are all c++ (algorithm side, business side basically go) Our so-called models are usually just trained to deploy and then provide interfaces, so now we finally know why it's not good to just be familiar with Python, cpp is yyds.

Like python, this series is a review, but it still doesn't sort out anything that's too basic. It's more like filling in gaps. However, c++ hasn't been used in 5 years for me. It's a big shortage, and it's almost a re-study, so let's go over it again in the next time😉

References are mainly C Language Chinese Network and C++ Tutorial by Guangcheng Then add your own understanding and programming experiments as a supplement to enhance your impression.

This article mainly studies the classes and objects of C++, which is also the key content of C++. This article has a lot of content, a long length, old rules, and you can get what you want. There must be something you know and feel like you've been gone before. That's interesting.

Mainly include:

  • C++ class and object understanding, will we create objects?
  • Member Variables and Functions of C++ Classes
  • C++ Class Member Access and Class Encapsulation
  • Memory Model and Function Compilation Principle of C++ Objects
  • Constructors and Destructors for C++.
  • Array of C++ Objects
  • C++ Member Objects and Closed Classes
  • this pointer in C++.
  • C++ Static Member Variables and Static Member Functions
  • const Reproduction of C++ Classes (Member Variables and Functions and const Objects)
  • Friend functions and classes in C++.
  • Class is also a scope
  • C++ string, important, so pick it up separately

Ok, let's go!

2.C++ Class and Object Understanding

With regard to classes, the first two points are:

  1. Classes are templates for creating objects. A class can create pairs of objects. Each object is a variable of the class type. The process of creating objects is also called instantiation of classes. Each object is a specific instance of a class and has member variables and member functions of the class.
  2. Classes, like structures, are just declarations of complex data types and do not take up memory space. Instead, objects are variables of such data types as classes, which create a real piece of data and therefore take up memory space.

For example:

class Student{
public:
    //Member variables
    char *name;
    int age;
    float score;
    //Member function
    void say(){
        cout<<name<<"Age is"<<age<<",The result is"<<score<<endl;
    }
};

The above class has three member variables and one member function. Class is only a Template and does not take up memory space after compilation, so member variables cannot be initialized when defining a class because there is no place to store data. Member variables are only allocated memory after the object is created, so they can be assigned.

Classes can be understood as a new data type, but unlike basic data types such as char,int, float, classes are complex data types in which members can contain basic types or have many features that the basic types do not have.

With such a class, you can create objects and access the members of the class through them.

Student zhongqiang;  // object

// Access object members can use.
zhongqiang.name
zhongqiang.age
zhongqiang.score
zhongqiang.say();

// Pointer access
Student *pstu = &zhongqiang;    // Give the address of zhongqiang to the pointer, pstu to zhongqiang

pstu -> name
pstu -> age
pstu -> score
pstu -> say()

Note here that the zhongqiang object above was created in the stack. It has a name, so pointing to it with a pointer is not necessary. The real beauty of the object pointer is the following.

If you want to create objects on the heap, you need to use the new keyword, then point to it with a pointer. This refers to the concept of stack memory and stack memory, which is not tidy up here, but tidy up later.

Student *pStu = new Student;

The object created with new is different. It allocates memory on the heap, has no name, and only gets a pointer to it. Therefore, you must use a pointer variable to receive the pointer, otherwise you can no longer find the object, nor can you use it. That is, use new.The object created on the heap is anonymous and cannot be used directly. It must be pointed to by a pointer, then accessed by the pointer to its member variables or member functions. When the object is not used, delete it

Student *pStu = new Student;
pStu -> name = "Xiao Ming";
pStu -> age = 15;
pStu -> score = 92.5f;
pStu -> say();
delete pStu;  //delete object

3.C++ Class Member Variables and Functions

Class This data type is a collection of member variables and member functions.

  • Class member variables, like regular variables, have data types and names, take up a fixed length, But cannot be assigned to member variables when defining a class, because a class is just a template
  • Like normal functions, member functions of a class have return values and parameter lists. But, unlike normal functions, member functions are members of a class that appear in the body of the class and whose scope is determined by the class.

Here's an example:

class Student{
public:
    //Member variables
    char *name;
    int age;
    float score;
    //Member function
    void say(){
        cout<<name<<"Age is"<<age<<",The result is"<<score<<endl;
    }
};

// The out-of-class definition of a function, where member functions are prototyped in the class body and then defined outside the class, where the position of the class body should precede the function definition.
class Student{
public:
    //Member variables
    char *name;
    int age;
    float score;
    //Member function
    void say();  //Function declaration
};
void Student::say(){
    cout<<name<<"Age is"<<age<<",The result is"<<score<<endl;
}

The above is an example of how functions in a class are defined in and out of the class body, which was not known before. There is a difference between the two, so here is new knowledge.

There is a difference between defining member functions within and outside a class: member functions defined within a class automatically become inline functions, but not outside the class. Inline functions are discussed in the first article. So here are some suggestions:

Inline functions are generally not what we expected, they will replace function calls with function bodies, so it is recommended that member functions be declared inside the class body and defined outside the class body. This is a good programming practice, and people do the same in actual development.

If the function is relatively short, it is also possible to define it as an inline function, which in this case can be defined within a class.

This is true, I see a lot of code inside large projects that are defining function logic out of class, and I find that it is common to define the declaration of a class into.h and then accompany a.cpp file to write the implementation logic of each member function.

4.C++ Class Member Access and Class Encapsulation

C++ controls access to member variables and member functions through the three keywords public, protected and private, indicating that public, protected, private, and made member access qualifiers. The so-called access rights refer to whether members in this class can be used or not.

  • Inside the class, members can access each other, regardless of whether they are declared public, protected or private, with no restrictions on access
  • Outside the class, members can only be accessed through objects, and only members of pubilc attributes can be accessed, not members of private s and protected attributes

Next🌰 It is more standard:

//Declaration of class
class Student{
private:  //Private
    char *m_name;
    int m_age;
    float m_score;
public:  //Common
    void setname(char *name);
    void setage(int age);
    void setscore(float score);
    void show();
};

// Definition of member function
void Student::setname(char *name){
    m_name = name;
}
void Student::setage(int age){
    m_age = age;
}
void Student::setscore(float score){
    m_score = score;
}
void Student::show(){
    cout<<m_name<<"Age is"<<m_age<<",The result is"<<m_score<<endl;
}

// Principal function
int main(){
    //Create objects on the stack
    Student stu;
    //stu.m_name = "Xiaoming";error private members cannot be accessed this way
    stu.setname("Xiao Ming");
    stu.setage(15);
    stu.setscore(92.5f);
    stu.show();
    //Create objects on the heap
    Student *pstu = new Student;
    pstu -> setname("Li Hua");
    pstu -> setage(16);
    pstu -> setscore(96);
    pstu -> show();
    return 0;
}

Creating objects on the stack and creating objects on the stack are mentioned earlier, but the comparison is clearer.

Class declarations and member function definitions are part of class definitions. In actual development, class declarations are usually placed in.h, while member function definitions are placed in the corresponding.cpp.

The member variables m_name, m_age and m_ score in a class are set as private attributes and cannot be accessed through objects outside the class. That is, private member variables and member functions can only be used inside the class and are invalid outside the class. The member functions setname(), setage(), and setscore() are set as public attributes and are public and accessible through objects.

Most of the member variables start with m_which is convention, not regulation. Starting with m_can be seen as a member variable at first glance, and can be distinguished from the name of a parameter in a member function.

The purpose of the private keyword is to better hide the internal implementation of a class. This exposed interface (a member accessible through an object) is declared public, and it is recommended that members not known from outside, used only within a class, or not affected externally be declared private. This also reflects an encapsulation feature.

Encapsulation refers to hiding the internal implementation of a class as much as possible and only providing useful member functions to the user.

Normally, we declare member variables of a class as private and not directly accessible to the outside world, but they can be accessed through functions within the class, which are usually public.

  • Functions that assign values to member variables are often called set functions, and their names usually begin with set, followed by the names of member variables.
  • Functions that read the values of member variables are often called get functions, and their names usually start with get followed by the names of member variables.

Also note that members declared as private and members declared as public can appear either first or first in any order. If you neither write private nor public, the default is private.

5.Memory Model and Function Compilation Principle of C++ Objects

5.1 Memory Model

Classes are templates for creating objects that do not take up memory space and do not exist in compiled executables; objects are real data that requires memory to store. When objects are created, memory is allocated in the stack or stack area.

So how do objects store in memory?

Visually, when 10 objects are created, memory is allocated to member variables and member functions of 10 objects, such as:

But if you do this, memory will not explode as many objects as you have, for example, class people. In fact, the values of member variables are only different among different objects, but the code of member functions used is the same. It is not necessary to store variable Han functions together in an object-space. So what does the compiler do?

The compiler stores member variables and member functions separately, allocates memory for each object's member variables, but all objects share a piece of function code, like this:


Then, a question arises at this point. If only member variables are retained in the object, and there is no other information, the program does not know that zhongqiang is a people type when it runs, nor does it know that it has member functions such as eat(), sleep(), etc., how can C++ call member functions through the object?

5.2 C++ Function Compilation

C++ and C are compiled differently. Functions in C are compiled with the same name or simply underlined (different compilers have different implementations), for example, func() is compiled as _func().

Functions in C++ are renamed at compile time based on information such as the namespace they are in, the classes they belong to, and their parameter lists to form a new function name. This new function name is known only to the compiler and is not visible to the user. The process of renaming a function is called name encoding (Name Mangling).Is implemented by a special algorithm.

Name Mangling's algorithm is reversible, either by calculating a new function name from an existing one or by deducing the original function name from a new one. Name Mangling ensures the uniqueness of the new function name, as long as the namespace in which the function resides, the class to which it belongs, the list of parameters contained, etc. are different, the resulting new function name will be different.

Take an example from the first reference document above to see what the new function name looks like:

In parentheses is the name of the new function encoded by the name, beginning with? To distinguish from in C. So after the function has a new name, and this member function is finally compiled into a global function independent of the object, how should the object be called?

5.3 Member Function Calls

If there are no member variables in the body of the function, the problem is very simple. Without any handling of the function, call it directly.C++ specifies that member functions are compiled with an additional parameter, passing in the pointer of the current object, and accessing member variables through the pointer.

For example, Demo:

void People::sleep(){
    cout<<a<<endl;
    cout<<b<<endl;
}

Then after compiling it becomes:

void new_function_name(People * const p){   // Note that this p is a const pointer and can only point to the current object, not to other objects
    //Access a, b through pointer p
    cout<<p->a<<endl;
    cout<<p->b<<endl;
}

When a function is called with zhongqiang.sleep(), it is also compiled to look like the following:

new_function_name(&obj);

The association between member functions and member variables is done by passing an object pointer. This is contrary to what we see from the indication. When a member function is called through an object, it is not through the object to find its member function, but through the member function to find the current object, but it is implicitly done and transparent to us.

6.C++ Constructors and Destructors

6.1 C++ Constructor

6.1.1 Constructor Declarations and Definitions

Constructor is a special member function in C++ class. It has the same name and class name. It has no return value and cannot be called explicitly. It is a function that is executed automatically when the object is created.

Constructors help us assign values to member variables while creating objects:

class Student{
private:
    char *m_name;
    int m_age;
    float m_score;
public:
    //Declare Constructor
    Student(char *name, int age, float score);
    //Declare normal member functions
    void show();
};
//Define Constructor
Student::Student(char *name, int age, float score){
    m_name = name;
    m_age = age;
    m_score = score;
}
//Define normal member functions
void Student::show(){
    cout<<m_name<<"Age is"<<m_age<<",The result is"<<m_score<<endl;
}
int main(){
    //Pass to constructor when creating object
    Student stu("Xiao Ming", 15, 92.5f);
    stu.show();
    //Pass to constructor when creating object
    Student *pstu = new Student("Li Hua", 16, 96);
    pstu -> show();
    return 0;
}

The student class above defines a constructor, Student(char *, int, float), that assigns values to members of the three private s. To call the constructor, you must pass arguments while creating the object.

When an object is created on the stack, arguments are placed after the object name, such as Student stu("Xiaoming", 15, 92.5f), and objects are created on the stack after the class name, such as new Student("Li Hua", 16, 96).

There are a few things to note about constructors (tap the blackboard):

  1. Constructors must be public attributes, or they cannot be used when creating objects, and although setting the private and protected attributes will not cause errors, it does not make sense.
  2. The constructor does not return a value because no variable receives a return value, even if it does, and so:
    1. Neither the declaration nor the definition precedes the function name with a return value type nor a void
    2. You cannot have a return statement in a function body

6.1.2 Constructor Overload

With regard to constructor overloading, a class can have multiple overloaded constructors, which are called when an object is created based on the passed arguments. Note, however, that the call to a constructor is mandatory, and once a constructor is defined in the class, it must be called when the object is created, without calling it incorrectly.If there are multiple overloaded constructors, the arguments provided when creating the object must match one of them. Conversely, only one constructor is called when creating the object.

like this:

class Student{
private:
    char *m_name;
    int m_age;
    float m_score;
public:
    Student();
    Student(char *name, int age, float score);
    void setname(char *name);
    void setage(int age);
    void setscore(float score);
    void show();
};
Student::Student(){
    m_name = NULL;
    m_age = 0;
    m_score = 0.0;
}
Student::Student(char *name, int age, float score){
    m_name = name;
    m_age = age;
    m_score = score;
}

Constructors are used extensively in practical development and are often used to do some initialization work, such as assigning member variables, opening files in advance, and so on.

If the user does not define a constructor, the compiler automatically generates a default constructor whose body is empty, has no parameters, and does nothing.

Student(){}

A class must have a constructor, either defined by the user or automatically generated by the compiler. Once the user has defined the constructor himself, the compiler will no longer automatically generate it, regardless of the number of constructors and parameters. Note that.

In fact, the compiler generates default constructors only when necessary, and their body is generally not empty. The purpose of default constructors is to help the compiler do initialization, not to help the programmer. This is the internal implementation mechanism of C++.

Finally, note that calling a constructor without parameters can also omit parentheses. For example, previous student *pstu = new Student or Student *pstu = new Student(), they both call the constructor Student ()

6.1.3 Constructor Initialization List

An important function of a constructor is to initialize member variables. To do this, member variables can be assigned to one another in the body of the constructor, or initialization lists can be used.

class Student{
private:
    char *m_name;
    int m_age;
    float m_score;
public:
    Student(char *name, int age, float score);
    void show();
};
//Take Initialization List
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){
    //TODO:
}

Instead of assigning one-to-one values to member variables in the body of the function, the body is empty (and there can be other statements, of course), a colon is added between the first part of the function and the body: followed by m_name(name), m_age(age), m_score(score), which means m_name = name inside the body of the function;M_age = age;M_score = score;Statement, also means assignment.

This is not an efficient way to write, especially when there are many member variables

Initialization lists can be used for all or only a few member variables.

Student::Student(char *name, int age, float score): m_name(name){
    m_age = age;
    m_score = score;
}

Note that the order in which member variables are initialized is independent of the order in which they are listed in the initialization list, but only the order in which member variables are declared in the class.

class Demo{
private:
    int m_a;
    int m_b;
public:
    Demo(int b);
    void show();
};
Demo::Demo(int b): m_b(b), m_a(m_b){ }
void Demo::show(){ cout<<m_a<<", "<<m_b<<endl; }
int main(){
    Demo obj(100);   // The result at this point is 239783947 100
    obj.show();
    return 0;
}

In the example above, when writing the initialization list, m_b is placed before m_a, which seems to assign m_b to m_a before m_a. In fact, it is not. The order in which member variables are copied is determined by the order in which they are declared in the class. In the demo class, m_a is declared before m_b, so the constructor is equivalent to the following:

Demo::Demo(int b): m_b(b), m_a(m_b){
    m_a = m_b;
    m_b = b;
}

When m_a is assigned, m_b is not initialized, so the value is not certain. So it is a random number. When m_a is assigned, m_b is assigned to m_b, which is 100.

obj allocates memory on the stack, and member variables have an indeterminate initial value

6.1.3 Initialize const member variable

Constructor initialization lists also play an important role in initializing const member variables. The only way to initialize a const member variable is to use an initialization list. Note that the only way to initialize a const member variable is to use an initialization list.

For example, VS/VC does not support variable-length arrays (array length cannot be a variable). We have defined a VLA class to simulate variable-length arrays. See the code below:

class VLA{
private:
    const int m_len;
    int *m_arr;
public:
    VLA(int len);
};
//Initialization list must be used to initialize m_len
VLA::VLA(int len): m_len(len){
    m_arr = new int[len];
}

The VLA class contains two member variables, the m_len and m_arr pointers. It is important to note that m_len is modified with const and can only be assigned using an initialization list if the following form is incorrect:

class VLA{
private:
    const int m_len;
    int *m_arr;
public:
    VLA(int len);
};
VLA::VLA(int len){
    m_len = len;
    m_arr = new int[len];
}

6.2 C++ Destructors

When an object is created, the system automatically calls a constructor to initialize it. Similarly, when an object is destroyed, the system automatically calls a function to clean up the work, such as releasing allocated memory, closing open files, etc. This function is a destructor.

Destructor is also a special member function that does not return a value and does not require an explicit call from the programmer (nor can the programmer explicitly call it), but is automatically executed when the object is destroyed. The name of the constructor is the same as the class name, while the name of the destructor is preceded by a ~sign.

Note: Destructors have no parameters and cannot be overloaded, so a class can only have one destructor. If not defined by the user, the compiler automatically generates a default destructor.

The above example is a good one, defining a VLA class to simulate variable-length arrays, which uses a constructor to allocate memory for arrays that do not automatically release when the array is destroyed, so it is necessary to add another destructor specifically designed to release memory that has already been allocated.

class VLA{
public:
    VLA(int len);  //Constructor
    ~VLA();  //Destructor
public:
    void input();  //Enter array elements from console
    void show();  //Show Array Elements
private:
    int *at(int i);  //Get the pointer to the ith element
private:
    const int m_len;  //Array length
    int *m_arr; //Array Pointer
    int *m_p;  //Pointer to the first element of the array
};
VLA::VLA(int len): m_len(len){  //Assigning m_len values using an initialization list
    if(len > 0){ m_arr = new int[len];  /*Allocate memory*/ }
    else{ m_arr = NULL; }
}
VLA::~VLA(){
    delete[] m_arr;  //Release memory
}
void VLA::input(){
    for(int i=0; m_p=at(i); i++){ cin>>*at(i); }
}
void VLA::show(){
    for(int i=0; m_p=at(i); i++){
        if(i == m_len - 1){ cout<<*at(i)<<endl; }
        else{ cout<<*at(i)<<", "; }
    }
}
int * VLA::at(int i){
    if(!m_arr || i<0 || i>=m_len){ return NULL; }
    else{ return m_arr + i; }
}
int main(){
    //Create an array (object) with n elements
    int n;
    cout<<"Input array length: ";
    cin>>n;
    VLA *parr = new VLA(n);
    //Input Array Element
    cout<<"Input "<<n<<" numbers: ";
    parr -> input();
    //Output Array Elements
    cout<<"Elements: ";
    parr -> show();
    //Delete Array (Object)
    delete parr;
    return 0;
}

New and delete in C++ are used to allocate and free memory, respectively, and one of the biggest differences between malloc(), free() in C is that the constructor is called when new allocates memory and the destructor is called when delete releases memory. Constructors and destructors are indispensable to classes.

Destructors are executed at different times for different objects, depending on the memory region in which they are destroyed.

  • Objects created outside all functions are global objects, which, like global variables, are in the global data area of a memory partition and are called by the program's destructor at the end of execution
  • Objects created inside a function are local objects, which, like local variables, are on the stack and call their destructors at the end of function execution
  • The object created by new is in the heap, and the destructor is called only when deleted. Without delete, the destructor will not execute.

Take the following example:

class Demo{
public:
    Demo(string s);
    ~Demo();
private:
    string m_s;
};
Demo::Demo(string s): m_s(s){ }
Demo::~Demo(){ cout<<m_s<<endl; }
void func(){
    //Local Objects
    Demo obj1("1");
}
//Global Objects
Demo obj2("2");
int main(){
    //Local Object Storage Stack
    Demo obj3("3");
    //new created object store heap
    Demo *pobj4 = new Demo("4");
    func();
    cout<<"main"<<endl;
  
    return 0;
}

// Result
1
main
3
2

The result here is that the func function, because it is a local object in the stack area, finishes execution of the function, calls first, outputs 1. Then outputs main. Then mains function finishes execution, when the stack area object is destroyed, destructors are executed, outputs 3, and objects created through new are stored in the stack, if you do not delete the object manually,The destructor is not called, so there is no 4 on it. If you change the main function to this one

int main(){
    //Local Objects
    Demo obj3("3");
    //Objects created by new
    Demo *pobj4 = new Demo("4");
    func();
    cout<<"main"<<endl;
    
    delete pobj4;
  
    return 0;
}

// Result
1
main
4
3
2

The destructor of the object in the heap will be called, so when the object using the heap memory is used up, remember to delete to free up memory.

7.Array of C++ Objects

C++ allows each element of an array to be an object, such as an array of objects.

Each element in an object array needs to be initialized with a constructor. Which elements are initialized with which constructors depends on how the array is defined.

#include<iostream>
using namespace std;
class CSample{
public:
    CSample(){  //Constructor 1
        cout<<"Constructor 1 Called"<<endl;
    }
    CSample(int n){  //Constructor 2
        cout<<"Constructor 2 Called"<<endl;
    }
};
int main(){
    cout<<"stepl"<<endl;
    CSample arrayl[2];
    cout<<"step2"<<endl;
    CSample array2[2] = {4, 5};
    cout<<"step3"<<endl;
    CSample array3[2] = {3};
    cout<<"step4"<<endl;
    CSample* array4 = new CSample[2];
    delete [] array4;
    return 0;
}

// Result
stepl    // This default call to parameterless constructor initialization because neither element is initialized
Constructor 1 Called
Constructor 1 Called
step2    // The {4,5} of the initialization list can be seen as an argument to initialize two array elements, so call the second constructor
Constructor 2 Called
Constructor 2 Called
step3   // Only array[0] was initialized
Constructor 2 Called
Constructor 1 Called
step4   // Neither is initialized
Constructor 1 Called
Constructor 1 Called

In fact, the above is not very clear, but I still feel that the following method is better, that is, when the constructor has more than one parameter, the initialization list of the array explicitly contains calls to the constructor.

class CTest{
public:
    CTest(int n){ }  //Constructor (1)
    CTest(int n, int m){ }  //Constructor (2)
    CTest(){ }  //Constructor (3)
};
int main(){
    //The three elements are initialized with constructors (1), (2), (3)
    CTest arrayl [3] = { 1, CTest(1,2) };
    //The three elements are initialized with constructors (2), (2), (1)
    CTest array2[3] = { CTest(2,3), CTest(1,2), 1};
    //Objects pointed to by two elements are initialized with constructors (1), (2)
    CTest* pArray[3] = { new CTest(4), new CTest(1,2) };
    return 0;
}

8.C++ Member Objects and Closed Classes

A member variable of one class is called a member object if it is an object of another class. A class containing member objects is called a closed class.

When I first read this sentence, I was a little unresponsive, so it was interesting to think of an example, such as a pregnant woman, a baby class. In the member variables of pregnant women, there will be objects of baby class. After all, baby is also a part of pregnant women. At this time, pregnant women are closed classes, and baby objects in pregnant women,Is the member object.

Of course, the following is an example drawn from the document. It feels better. I think this is easy to understand.

Initialization of 8.1 member objects

When creating a closed class object, it also contains member objects that need to be created, which can trigger calls to member object constructors. How can the compiler know which constructor a member object is initialized with? This requires an initialization list of closed class constructors.

//Tyres
class Tyre{
public:
    Tyre(int radius, int width);
    void show() const;
private:
    int m_radius;  //radius
    int m_width;  //width
};
Tyre::Tyre(int radius, int width) : m_radius(radius), m_width(width){ }
void Tyre::show() const {
    cout << "Hub radius:" << this->m_radius << "Inches" << endl;
    cout << "Tyre width:" << this->m_width << "mm" << endl;
}

//Engine Class
class Engine{
public:
    Engine(float displacement = 2.0);
    void show() const;
private:
    float m_displacement;
};
Engine::Engine(float displacement) : m_displacement(displacement) {}
void Engine::show() const {
    cout << "Displacement:" << this->m_displacement << "L" << endl;
}

//Cars
class Car{
public:
    Car(int price, int radius, int width);
    void show() const;
private:
    int m_price;  //Price

	// Here both member objects are objects of the class
    Tyre m_tyre;
    Engine m_engine;
};
Car::Car(int price, int radius, int width): m_price(price), m_tyre(radius, width)/*Indicates how the m_tyre object is initialized*/{ };
void Car::show() const {
    cout << "Price:" << this->m_price << "¥" << endl;
    this->m_tyre.show();
    this->m_engine.show();
}

int main()
{
    Car car(200000, 19, 245);
    car.show();
    return 0;
}

In this example, Car is a closed class with object members m_tyre and m_engine of two classes. When creating a car object, the compiler needs to know how the duo m_tyre and m_engine should be initialized.

The compiler uses the initialization list of the constructor of the Car class above, and the car object is initialized by the Car(int price, int radius, int width) constructor. How m_tyre and m_engine are initialized depends on the subsequent initialization list. The initialization list indicates:

  • m_tyre invokes the Tyre(int radius, int width) constructor initialization with radius and width as parameters.
  • However, this does not explain how m_engine should be handled. In this case, the compiler assumes that m_engine should be initialized with the parameterless constructor of the Engine class. The Engine class does have a parameterless constructor (because default parameters are set), so the initialization of the entire car object is resolved.

In summary, statements that generate enclosed classes must enable the compiler to understand how member objects are initialized, otherwise compilation errors will occur.

In the above program, if the constructor of the Car class does not have an initialization list, the line where the car object is created will compile errors because the compiler does not know how to initialize the car.m_tyre object, because the Tyre class does not have a parameterless constructor, and the compiler cannot find the parameters to initialize the car.m_tyre object.

8.2 Extinction of member objects

When a closed class object is generated, the constructor for all member objects is executed before the closed class's own constructor is executed. The order in which member object constructors are executed is the same as the order in which member objects are defined in the class, regardless of the order in which they appear in the constructor initialization list.

When an enclosed class object dies, the destructor of the enclosed class is executed first, then the destructor of the member object. The order of execution of the member object destructor is the opposite of that of the constructor, that is, the destructor is constructed first, then the destructor. This is the general rule for C++ to handle this kind of ordering problem.

Look at the following code:

#include<iostream>
using namespace std;
class Tyre {
public:
    Tyre() { cout << "Tyre constructor" << endl; }
    ~Tyre() { cout << "Tyre destructor" << endl; }
};
class Engine {
public:
    Engine() { cout << "Engine constructor" << endl; }
    ~Engine() { cout << "Engine destructor" << endl; }
};
class Car {
private:
    Engine engine;
    Tyre tyre;
public:
    Car() { cout << "Car constructor" << endl; }
    ~Car() { cout << "Car destructor" << endl; }
};
int main() {
    Car car;
    return 0;
}

// Result
Engine constructor
Tyre constructor
Car constructor
Car destructor
Tyre destructor
Engine destructor

9.C++ this pointer

This is a keyword and a const pointer in C++. It points to the current object through which all members of the current object can be accessed. If you are familiar with Python, you know the self in Python. Analogue to c++ is this.

class Student{
public:
    void setname(char *name);
    void show();
private:
    char *name;
};
void Student::setname(char *name){
    this->name = name;
}

void Student::show(){
    cout<<this->name<<endl;
}
int main(){
    Student *pstu = new Student;
    pstu -> setname("Li Hua");
    pstu -> show();
    return 0;
}

For this, we need to know the following first:

  • this can only be used inside a class, through which all members of the class, including private, protected, and public attributes, can be accessed. But it is not part of the object itself, it just points to the object.
  • This is used inside a class, but it is only assigned after the object is created, and this assignment is done automatically by the compiler, without user intervention, and the user cannot explicitly assign this.
  • This is a const pointer (A * const), whose value cannot be modified. Any attempt to modify this pointer, such as assignment, increment, decrement, etc., is not allowed.
  • this can only be used inside member functions and is meaningless and illegal elsewhere.
  • this makes sense only when the object is created, so it cannot be used in the static member function.
  • Note that this is a pointer to access member variables or member functions with ->.

The essence of this:

This is actually a parameter of a member function that passes the object's address as an argument when the member function is called. However, this parameter is implicit and does not appear in the code, but is silently added to the parameter list by the compiler during the compilation phase.

For example, the setname(char *name) function above will be setname(Student *const this, char *name) when compiled.As mentioned in the compilation principle of member functions above, member functions are eventually compiled into object-independent normal functions, and all information is lost except member variables. Therefore, at compilation time, an additional parameter is added to the member functions to pass in the first address of the current object in order to associate member functions with member variables. This additional parameter is actuallyThis is the bridge between member functions and member variables.

So this, as an implicit parameter, is essentially a local variable of a member function, so it can only be used inside a member function and only be assigned to this if the member function is called through an object.

10.Static member variables and static member functions in C++.

10.1 Static member variable

Static member variable is a special member variable, which is modified by the keyword static. In C++, we can use static member variable to achieve the goal of sharing data among multiple objects.

class Student{
public:
    Student(char *name, int age, float score);
    void show();
public:
    static int m_total;  //Static member variable
private:
    char *m_name;
    int m_age;
    float m_score;
};

This statement above declares a static variable to count the number of classes. Shared for each student in the class. So for static-modified member variables, note the following points (highlights):

  • static member variables are classes and do not belong to a specific object. Even when multiple objects are created, only one piece of memory is allocated for m_total, and all objects use the data in that memory. When an object modifies m_total, it also affects other objects.

  • Static member variables must be initialized outside the class declaration in the form of type class::name = value;Because it is a class, it cannot be initialized through a constructor. Static member variables cannot be initialized with static, but must have a data type. Static member variables decorated with private, protected, and public can be initialized in this way. Note:staticThe memory of a member variable is allocated neither when declaring a class nor when creating an object, but when initializing (out of class). Conversely, a static member variable that is not initialized out of class cannot be used.

  • static member variables can be accessed either by a class or by a class

    //Accessing static member variables through class classes
    Student::m_total = 10;
    //Accessing static member variables through objects
    Student stu("Xiao Ming", 15, 92.5f);
    stu.m_total = 20;
    //Accessing static member variables through object pointers
    Student *pstu = new Student("Li Hua", 16, 96);
    pstu -> m_total = 20;
    

    Note: Static member variables do not occupy the memory of the object, but open up memory outside of all objects, even if the object is not created. Specifically, static member variables are similar to normal static variables in that they allocate memory in the global data area in the memory partition

Here is an example of using static member variables to count the number of students:

class Student{
public:
    Student(char *name, int age, float score);
    void show();
private:
    static int m_total;  //Static member variable
private:
    char *m_name;
    int m_age;
    float m_score;
};
//Initialize static member variables, which are initialized this way
int Student::m_total = 0;
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){
    m_total++;  //Operating on static member variables
}
void Student::show(){
    cout<<m_name<<"Age is"<<m_age<<",The result is"<<m_score<<"(Currently Common"<<m_total<<"Students)"<<endl;
}
int main(){
    //Create anonymous objects
    (new Student("Xiao Ming", 15, 90)) -> show();
    (new Student("Li Lei", 16, 80)) -> show();
    (new Student("Zhang Hua", 16, 99)) -> show();
    (new Student("Wang Kang", 14, 60)) -> show();
    return 0;
}

// Result
 Xiao Ming's age is 15 and his score is 90 (there is currently a student in total)
Li Lei is 16 years old and has achieved 80 (currently there are 2 students)
Zhang Hua is 16 and has a score of 99 (currently there are 3 students)
Wang Kang is 14 and has a score of 60 (currently there are 4 students)

In this example, m_total is declared as a static member variable, and each time an object is created, the constructor is called to add a value of m_total to 1.

Anonymous objects are used because each time an object is created, only its show() function is used and no other operations are performed. However, using anonymous objects does not reclaim memory, which can lead to memory leaks and is not recommended in large and medium-sized programs.

In a nutshell:

  • A class may have one or more static member variables that all objects share and can reference.
  • Static member variables, like ordinary static variables, allocate memory in the global data area of the memory partition until the end of the program. This means that static member variables do not allocate memory with the creation of objects or with the destruction of objects. Ordinary member variables allocate memory at object creation and release memory when objects are destroyed.
  • Static member variables must be initialized and can only be initialized outside the class. They can be initialized with or without an initial value. If not, they will be initialized by default to 0. Variables in the global data area have a default initial value of 0, while variables in the dynamic data area (stack area, stack area) have an indeterminate default value and are generally considered garbage.
  • Static member variables can be accessed either by object name or by class name, but subject to the access restrictions of the private, protected, and public keywords. When accessed by object name, the same memory is accessed for different objects.

10.2 Static Member Functions

In a class, static can declare static member functions in addition to static member variables. Normal member functions can access all members, including member variables and member functions, while static member functions can only access static members.

When compiling a normal member function, the compiler implicitly adds a parameter this and assigns the address of the current object to it, so a normal member function can only be called through the object after it is created because it requires the address of the current object. Static member functions can be called directly through classes, and the compiler does not add parameters to itThis does not require the address of the current object, so static member functions can be called regardless of whether or not the object is created.

The basic difference between a static member function and a normal member function is that a normal member function has a this pointer and can access any member of a class, while a static member function does not have a this pointer and can only access static members (including static member variables and static member functions).

In C++, the primary purpose of static member functions is to access static members. Take a complete example:

class Student{
public:
    Student(char *name, int age, float score);
    void show();
public:  //Declare static member functions
    static int getTotal();
    static float getPoints();
private:
    static int m_total;  //Total number
    static float m_points;  //Total results
private:
    char *m_name;
    int m_age;
    float m_score;
};
int Student::m_total = 0;
float Student::m_points = 0.0;
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){
    m_total++;
    m_points += score;
}
void Student::show(){
    cout<<m_name<<"Age is"<<m_age<<",The result is"<<m_score<<endl;
}
//Define static member functions
int Student::getTotal(){
    return m_total;
}
float Student::getPoints(){
    return m_points;
}
int main(){
    (new Student("Xiao Ming", 15, 90.6)) -> show();
    (new Student("Li Lei", 16, 80.5)) -> show();
    (new Student("Zhang Hua", 16, 99.0)) -> show();
    (new Student("Wang Kang", 14, 60.8)) -> show();
    int total = Student::getTotal();
    float points = Student::getPoints();
    cout<<"Currently Common"<<total<<"Student, total score is"<<points<<",Average score is"<<points/total<<endl;
    return 0;
}

Total number m_total and total score m_points are aggregated by each object and must be declared static in order to share; getTotal(), getPoints() are used to obtain total number and total score, respectively. To access the static member variable, we also declare these two functions static.

getTotal(), getPoints() can also be declared as normal member functions, but they all operate only on static members, with more explicit static semantics.

Similar to static member variables, static member functions are declared with static and cannot be defined with static. Static member functions can be invoked either by classes (this is generally the case) or by objects.

11.Const member variables, member functions, and objects

11.1 const member variables and member functions

The use of const member variables is similar to that of ordinary const variables, only the const keyword needs to be added to the declaration. There is only one way to initialize a const member variable, that is, to initialize a list through a constructor.

The const member function can use all the member variables in the class, but it cannot modify their values. This measure is mainly set to protect the data. The const member function is also called a constant member function. Here's how to use it:

class Student{
public:
    Student(char *name, int age, float score);
    void show();
    //Declare Constant Member Functions
    char *getname() const;
private:
    char *m_name;
};
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
void Student::show(){
    cout<<m_name<<"Age is"<<m_age<<",The result is"<<m_score<<endl;
}
//Define a constant member function
char * Student::getname() const{
    return m_name;
}

It is important to note that the const keyword must be added to both the declaration and definition of member functions. char *getname() const and char *getname() are two different function prototypes, and adding const in only one place can result in conflicting function prototypes at the declaration and definition.

Finally, let's distinguish the const location:

  • The const at the beginning of the function modifies the return value of the function, indicating that the return value is of type const, that is, it cannot be modified, such as const char * getname().
  • The end of the function header plus const denotes a constant member function, which can only read the values of member variables, but cannot modify the values of member variables, such as char * getname() const.

11.2 const object

In C++, const can also be used to modify objects, called constant objects. Once an object is defined as a constant object, only the const members of the class (including const member variables and const member functions) can be called.

const  class  object(params);
const class *p = new class(params);

Once an object is defined as a constant object, in either form, it can only access const-modified members (including const member variables and const member functions), since non-const members may modify the object's data (as the compiler assumes), which C++ prohibits.

Enumerate🌰

class Student{
public:
    Student(char *name, int age, float score);
public:
    void show();
    char *getname() const;
private:
    char *m_name;
};
Student::Student(char *name): m_name(name){ }
void Student::show(){
    cout<<m_name<<endl;
}
char * Student::getname() const{
    return m_name;
}

int main(){
    const Student stu("Xiao Ming");
    //stu.show();  //error
    cout<<stu.getname()<<endl;
    const Student *pstu = new Student("Li Lei");
    //pstu -> show();  //error
    cout<<pstu->getname()<<endl;
    return 0;
}

In this example, stu and pstu are constant objects and pointers to constant objects, respectively, and they can only call const member functions.

12.Friend functions and classes in C++.

In C++, members of a class can have three attributes: public, protected and private. Public members can be accessed by objects, and only functions in this class can access private members of this class. Friends are an exception. With friend sAllows member functions in other classes and functions in the global scope to access privatemembers of the current class.

12.1 Friend Function

Functions defined outside the current class that do not belong to the current class can also be declared in the class, but a friend function is constructed by preceding it with the friendly keyword. A friend function can be either a non-member function that does not belong to any class or a member function of another class.

Friend functions have access to all members of the current class, including public, protected, and private attributes.

There are two main situations:

  1. Nonmember function declared as friend function

    class Student{
    public:
        Student(char *name, int age, float score);
    public:
        friend void show(Student *pstu);  //Declare show() as a friend function
    private:
        char *m_name;
        int m_age;
        float m_score;
    };
    Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
    //Nonmember function
    void show(Student *pstu){
        cout<<pstu->m_name<<"Age is "<<pstu->m_age<<",The result is "<<pstu->m_score<<endl;
    }
    int main(){
        Student stu("Xiao Ming", 15, 90.6);
        show(&stu);  //Call friend function
        Student *pstu = new Student("Li Lei", 16, 80.5);
        show(pstu);  //Call friend function
        return 0;
    }
    

    Show() is a global non-member function that does not belong to any class and serves to output student information. m_name, m_age, m_scope are private members of the Student class and in principle cannot be accessed through objects, but these private members must be used in the show() function, so show() is declared as a friend function of the Student class.

    Note that a friend function is different from a class's member function in that it cannot directly access the members of the class, it must be aided by an object, if so:

    void show(){
        cout<<m_name<<"Age is "<<m_age<<",The result is "<<m_score<<endl;
    }
    

    No, a member function implicitly adds a this pointer to the object calling it so that the member of the object is used; while show() is a non-member function without a this pointer, and the compiler does not know which object to use. To make this clear, you must pass the object by argument(You can pass objects directly, object pointers or object references), and specify objects when accessing members.

  2. A member function of a class is declared as a friend function

    The friend function can be not only a global function (a non-member function), but also a member function of another class.

    class Address;  //Declare Address class in advance
    //Address add //error declares in advance that an object cannot be created
    
    //Declare Student Class
    class Student{
    public:
        Student(char *name, int age, float score);
    public:
        void show(Address *addr);
    private:
        char *m_name;
        int m_age;
        float m_score;
    };
    //Declare Address Class
    class Address{
    private:
        char *m_province;  //Province
        char *m_city;  //City
        char *m_district;  //District (Urban Area)
    public:
        Address(char *province, char *city, char *district);
        //Declare the member function show() in the Student class as a friend function
        friend void Student::show(Address *addr);
    };
    //Implement Student Class
    Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score){ }
    void Student::show(Address *addr){
        cout<<m_name<<"Age is "<<m_age<<",The result is "<<m_score<<endl;
        cout<<"Home address:"<<addr->m_province<<"province"<<addr->m_city<<"city"<<addr->m_district<<"area"<<endl;
    }
    //Implement Address Class
    Address::Address(char *province, char *city, char *district){
        m_province = province;
        m_city = city;
        m_district = district;
    }
    int main(){
        Student stu("Xiao Ming", 16, 95.5f);
        Address addr("Shaanxi", "Xi'an", "Yan pagoda");
        stu.show(&addr);
       
        Student *pstu = new Student("Li Lei", 16, 80.5);
        Address *paddr = new Address("HEBEI", "Hengshui", "Taocheng");
        pstu -> show(paddr);
        return 0;
    }
    

    The following points should be noted:

    1. The Address class is declared beforehand because it is used in the Student class before the Address class is defined, and if it is not declared beforehand, the compiler will error, prompting'Address'has not been declared. The beforehand declaration of the class and the beforehand declaration of the function are reasonable.
    2. The program separates the declaration and implementation of the Student class and places the declaration of the Address class in the middle because the compiler compiles code from top to bottom, and members of Address, province, city, region, are used in the show() function body. If you do not know in advance what the specific declaration of Address is, you cannot determine if Address owns that member.In general, a class must be declared before it can be used, but in some cases (as shown in the example above), it can be used as long as it has been declared in advance. However, such an advance declaration cannot create an object, such as the second line above, and the compiler will fail

      To allocate memory for an object when it is created, the compiler cannot determine how much memory it should allocate to an object until the class is formally declared. The compiler only sees the formal declaration of the class (actually the member variable)To determine how much memory should be reserved for an object, you can use the name of a class to define pointer variables (in this case, pointer variables for Address classes) or reference variables that point to that type of object after an earlier declaration, because the size of the pointer and reference variables themselves is fixed and independent of the size of the data they point to.

    3. A function can be declared as a friend function by multiple classes, which allows access to private s in multiple classes

12.2 Friend Class

Not only can a function be declared as a "friend" of one class, but the whole class can also be declared as a "friend" of another class, which is a friend class. All member functions in a friend class are friend functions of another class.

For example, if class B is declared as a friend class of class A, then all member functions in class B are friend functions of class A and can access all members of class A, including the public, protected, and private attributes.

In the example above, if you declare Student as a friend of Address, you can also access members of Address through Student's Show:

//Declare Address Class
class Address{
public:
    Address(char *province, char *city, char *district);
public:
    //Declare the Student class as a friend of the Address class
    friend class Student;
private:
    char *m_province;  //Province
    char *m_city;  //City
    char *m_district;  //District (Urban Area)
};

As for Friends, the following points should be noted:

  1. Friend relationships are one-way rather than two-way. If class B is declared as a friend class of class A, it is not equal to class A being a friend class of class B, and member functions in class A cannot access private s in class B.
  2. Friend relationships cannot be transferred. If class B is the friend class of class A, class C is the friend class of class B, not equal to class C being the friend class of class A.
  3. It is not recommended to declare an entire class as a friend class of another class, but to declare only certain functions of the class as friend functions, which is safer

13.Class, in fact, is also a scope

Classes are also scopes, and each class defines its own scope. Outside the scope of a class, ordinary members can only be accessed by objects (either objects themselves, object pointers, or object references). Static members can be accessed by objects as well as classes, while types defined by typedef can only be accessed by classes.

Take an example:

class A{
public:
    typedef int INT;     // This is a new name for the int type
    static void show();
    void work();
};
void A::show(){ cout<<"show()"<<endl; }
void A::work(){ cout<<"work()"<<endl; }
int main(){
    A a;
    a.work();  //Accessing ordinary members through objects
    a.show();  //Accessing static members through objects
    A::show();  //Accessing static members through classes
    A::INT n = 10;  //Accessing typedef-defined types through classes
    return 0;
}

The fact that a class is a scope explains why we must provide both the class name and the function name when defining member functions outside the class. Outside the class, the names of members inside the class are not visible.

Once the class name is encountered, the rest of the definition is within the scope of the class, where the rest includes the list of parameters and the body of the function. The result is that we can use other members of the class directly without having to authorize them again.

class A{
public:
    typedef char* PCHAR;
public:
    void show(PCHAR str);
private:
    int n;
};
void A::show(PCHAR str){
    cout<<str<<endl;
    n = 10;
}
int main(){
    A obj;
    obj.show("http://c.biancheng.net");
    return 0;
}

We use a type of PCHAR defined in class A when defining the show() function, since it has been previously indicated that it is currently in the scope of class A, we no longer need to use redundant forms such as A::PCHAR. Similarly, the compiler knows that the variable n used in the body of the function is also in the scope of class A.

But, the return value type of a function occurs before the function name. When a member function is defined outside a class, the name used in the return value type is outside the scope of the class, and you must specify which class the name is a member of. Modify the show() function above so that its return value type is PCHAR:

PCHAR A::show(PCHAR str){
    cout<<str<<endl;
    n = 10;
    return str;
}

This is incorrect because the return value type PCHAR appears before the class name, so it is actually outside the scope of class A. In this case, to use PCHAR as the return value type, you must specify which class defines it, and the correct writing is as follows:

A::PCHAR A::show(PCHAR str){
    cout<<str<<endl;
    n = 10;
    return str;
}

14.Strings in C++

string is a common class in C++ and it is very important, so sort it out separately.

14.1 Basic Definitions

Using a string class requires a header file <string>. The following examples show several ways to define string variables (objects):

#include <iostream>
#include <string>
using namespace std;
int main(){
    string s1;  // The variable S1 is only defined but not initialized, and the compiler assigns the default value to s1, which is ", or an empty string
    string s2 = "c plus plus"; // Variable s2 is initialized to "c plus plus plus" at the same time it is defined. Unlike C-style strings, strings end with no end flag'\0'.
    string s3 = s2; // The variable s3 is initialized directly with s2 when it is defined, so the content of s3 is also "c plus plus plus".
    string s4 (5, 's'); // The variable s4 is initialized as a string of five's'characters, known as's s s s s'.
    return 0;
}

String variables can be assigned directly by assignment operator = or by C-style strings, for example, s2 is initialized by a string constant, and s3 is initialized by an s2 variable.

Unlike C-style strings, we can call the length() function provided by the string class when we need to know the length of the string

string s = "http://c.biancheng.net";
int len = s.length();
cout<<len<<endl;  // 22 This one has no\0, so it's not length+1

14.2 C Style Conversion

Although C++ provides string classes to replace strings in the C language, it is sometimes necessary to use C-style strings (such as paths when opening files) in actual programming. For this reason, the string class provides us with a conversion function, c_str(), that converts a string to a C-style string and returns the const pointer of the string.(const char*). See the following code:

string path = "D:\\demo.txt";
FILE *fp = fopen(path.c_str(), "rt");

In order to open a file using the fopen() function in C, string strings must be converted to C-style strings.

Input and output of 14.3 string string

The string class overloads the input and output operators and treats string variables as normal variables, that is, with > > as input and < as output.

int main(){
    string s;
    cin>>s;  //Input string
    cout<<s<<endl;  //Output string
    return 0;
}

14.4 Access

string strings can also be accessed as C-style strings with subscripts. string strings still start with subscripts of 0.

int main(){
    string s = "1234567890";
    for(int i=0,len=s.length(); i<len; i++){
        cout<<s[i]<<" ";
    }
    cout<<endl;
    s[5] = '5';
    cout<<s<<endl;
    return 0;
}

14.5 stitching

With the string class, we can use + or += operators to stitch strings directly, which is very convenient. We no longer need to use functions such as strcat(), strcpy(), malloc() in C to stitch strings, and we no longer need to worry about running out of space.

When concatenating strings with +, the operator can have strings on either side, strings and a C-style string, strings and an array of strings, or strings and a single character

string s1 = "first ";
string s2 = "second ";
char *s3 = "third ";
char s4[] = "fourth ";
char ch = '@';
string s5 = s1 + s2; // first second
string s6 = s1 + s3; // first third
string s7 = s1 + s4; // first fourth
string s8 = s1 + ch; // first @

14.6 Additions, deletions and changes

The string class provided by C++ contains several useful member functions, which make adding, deleting, changing and querying strings much easier.

  1. Insert String
    The insert() function can insert another string at the position specified in the string string. One of its prototypes is string & insert (size_t pos, const string & str);

    int main(){
        string s1, s2, s3;
        s1 = s2 = "1234567890";
        s3 = "aaa";
        s1.insert(5, s3);
        cout<< s1 <<endl;  // 12345aaa67890
        s2.insert(5, "bbb");
        cout<< s2 <<endl;  // 12345bbb67890
        return 0;
    }
    
  2. Delete String
    The erase() function deletes a substring in a string, a prototype of the function: string &erase (size_t pos = 0, size_t len = npos); if len is not specified, delete all characters from pos to the end of the string directly

    int main(){
        string s1, s2, s3;
        s1 = s2 = s3 = "1234567890";
        s2.erase(5);   // 12345
        s3.erase(5, 3);  // 1234590
        return 0;
    }
    
  3. Extract String
    The substr() function is used to extract a substring from a string string, and its prototype is string substr (size_t pos = 0, size_t len = npos) const;,

    int main(){
        string s1 = "first second third";
        string s2;
        s2 = s1.substr(6, 6);  // second
        return 0;
    }
    

    These functions above throw exceptions if pos is out of bounds, but if len is out of bounds, all characters from pos to the end of the string are extracted.

14.7 Find

Three functions are described here:

  1. The find() function is used to find the location of substrings in a string. The prototype of the function is size_t find (const string & str, size_t POS = 0) const.

    int main(){
        string s1 = "first second third";
        string s2 = "second";
        int index = s1.find(s2,5);
        if(index < s1.length())
            cout<<"Found at index : "<< index <<endl;   // 6
        else
            cout<<"Not found"<<endl;
        return 0;
    }
    

    The find() function eventually returns the start subscript of the substring that first appears in the string. If no find is found, an infinite number is returned.

  2. rfind() function, similar to find(), but looking backwards and forwards

  3. The find_first_of() function is used to find the first occurrence of a character in a string that is common to both substrings and strings.

    int main(){
        string s1 = "first second second third";
        string s2 = "asecond";
        int index = s1.find_first_of(s2);
        if(index < s1.length())
            cout<<"Found at index : "<< index <<endl;   // 3 Shared's'
        else
            cout<<"Not found"<<endl;
        return 0;
    }
    

14.7 Deeper Story - What's inside a string?

In C, there are two ways to represent strings:
One is to use an array of characters to hold strings, such as char str[10] = "abc", which are readable and writable;
One is to use string constants, such as char *str = "abc", which are read-only and cannot be written.

Both forms always end with0.

C++ string s are distinct from their predecessors in C.

  • First, and most important, C++ string hides the physical representation of the character sequence it contains. Programmers don't have to worry about the dimensions or\0 aspects of arrays.

  • String encapsulates memory and capacity-related information internally. Specifically, C++ string objects know where they start in memory, the character sequence they contain, and the length of the character sequence; when memory is low, strings also automatically adjust to allow memory space to grow large enough to accommodate all character sequences.

This practice of C++ string greatly reduces the three most common and destructive errors in C programming:

  • Array out of bounds;
    Access array elements through pointers that are not initialized or assigned incorrect values;
    Released the memory occupied by the array, but left the "dangling" pointer.

  • The C++ standard does not define the memory layout of the string class. Each compiler manufacturer can provide different implementations, but must ensure that strings behave consistently. This is done for flexibility.

  • In particular, the C++ standard does not define exactly where memory space should be allocated for string objects to store character sequences. String memory allocation rules explicitly specify that reference counting is allowed but not required.In C, each character array occupies its own physical store. In C+, several separate stringsObjects may or may not occupy their own specific physical storage, but if reference counting is used to avoid saving copies of the same data, individual objects (in processing) must look and behave as if they had their own storage exclusively. See below.🌰

    int main() {
        string s1("12345");
        string s2 = s1;
        cout << (s1 == s2) << endl;  // 1
        s1[0] = '6';
        cout << "s1 = " << s1 << endl;  //62345
        cout << "s2 = " << s2 << endl;  //12345
        cout << (s1 == s2) << endl;   // 0
        return 0;
    }
    

    Each copy is created only when the string is modified, which is called a copy-on-write policy. This saves time and space when the string is used only as a value parameter or in other read-only situations.

This article should be considered as the last note during the internship. There are still a few days to finish the internship before you go back to school and continue learning and recording. With the basis of these two chapters, you can almost finish your work first. You will be on your last shift in the last few days.

Topics: Python C++