Summary of basic knowledge points that C++ must memorize


Key point: It can be mastered without any external information, and the exam and interview are likely to be involved.
Mastery: It can be mastered without any external materials, which may be involved in exams and interviews.
Familiarity: It can be mastered with appropriate reference materials, which may be involved in exams and interviews.
Understand: But the reference materials are mastered, and the exam is almost not taken. If the interview involves a simple chat, it is enough.
 

1. Introduction to C++

1. Why learn C++?

reason one

C++ is one of the most representative languages ​​for object-oriented programming. You can learn new programming ideas through C++ courses.

Reason 2

Before programming, there was no interface. Qt is a commonly used graphical user interface programming framework in embedded systems, and Qt is based on C++.

Reason three

The direction of C++ and Qt is relatively less related to the previous courses, and can form an employment direction independently.

2. Positioning of C++ courses

The C++ course is biased towards theory and requires learning grammar, don't think about why. Qt is a practical course of C++, and your why will be answered.

3. The development of C++ (understand)

In 1983, Bjarne Stroustrup of Bell Labs invented C++. C++ is an object-oriented programming (OOP) language that has been expanded and improved on the basis of the C language.

"The name symbolizes a natural evolution from changes in the C language," Stroustrup said. It was called "new C" when it was still in the development stage, and then it was called "C with Class". C++ is regarded as the superstructure of C language. In 1983, Rick Mascitti suggested to use the name C++, which is derived from the "++" operator (variable self-increment) in C language. Also in the common naming convention, a "+" is used to indicate an enhanced program.

It is often used in system development, engine development, embedded development and other application fields, and it is still one of the most popular programming languages ​​among programmers.

4. Features of C++ (familiarity)

• On the basis of supporting C, fully support object-oriented programming

• Wide range of programming fields and powerful functions (the most powerful programming language, none of them)

•Standards are kept updated, currently commonly used basic standards are ISO C++98 standard, ISO C++11 standard, etc.

• One of the few object-oriented languages ​​that support low-level operations

•Extremely efficient execution in object-oriented languages

5. The concept of object-oriented programming (master)

Object-oriented thinking needs to be gradually understood in the process of learning. Object-oriented has three core characteristics:

encapsulation → inheritance → polymorphism

Object-oriented also has two most basic concepts:

class, object

【Example】If you want to put an elephant in the refrigerator, what should you do?

Process-oriented programming ideas:

1. (I) open the refrigerator door

2. (I) put the elephant in it

3. (I) close the refrigerator door

Object-oriented programming ideas:

1. (I) Call the Elephant and the Refrigerator Over

2. (I) give tasks to the elephant and the refrigerator

3. Elephants and refrigerators assemble themselves

A process-oriented language focuses on "algorithms", and "algorithms" can be considered as a series of ordered steps. As long as you follow the steps, you can get the expected results. Therefore, programs written in process-oriented programming are usually a series of ordered statements, which is more in line with the nature of computer execution commands. It is characterized by low development efficiency and high operation efficiency.

The object-oriented language focuses on "management". Programmers need to manage various objects in the program. Objects can be considered as a series of data that are gathered together due to certain connections. It can be considered that in object-oriented languages, programmers focus on managing the relationship between data, which is closer to the way of thinking in human society. It is characterized by high development efficiency and low operation efficiency.

C++ is compatible with process-oriented and object-oriented, but object-oriented.

6. Environment installation

C++ programming can use many integrated development environments (IDEs). This course has no fixed requirements, but considering the subsequent Qt courses, it is recommended to use Qt's integrated development environment directly: Qt Creator

The version used in this teaching is 5.2.1, not the latest version.

The installation steps of this version environment are very simple, just go to the next step, but it should be noted that any path and file name must not contain Chinese characters .

In order to make Qt Creator support C++ Chinese output, after the installation is complete, modify the file encoding first, as shown below.

After the modification is completed, for the sake of safety, it is recommended to close Qt Creator and reopen it.

7. Your first C++ program

The steps to create and run a C++ project are as follows.

1. After starting Qt Creator, click

2. In the pop-up window, perform operations as shown in the figure below.

3. After entering the project name and setting the path of the working directory in the pop-up window, click "Next".

4. In the pop-up window, click "Next".

5. On the project management interface, click Finish directly.

6. You can see that the project is created and comes with a C++ source code file, click to run the project.

main.cpp

// 标准输入输出流头文件,C++自带的头文件使用尖括号,不写.h
#include <iostream>

// 使用std名字空间(后面会讲,不要删)
using namespace std;

/**
 * @brief main 主函数,程序的入口
 */
int main()
{
      
      
    // 输出一个字符串后,再输出一个换行(endl)
    cout << "Hello World!" << endl;
    cout << "你好世界!" << endl;

    return 0;
}

In addition to main.cp, there is also a .pro file, which is used to configure the parameters of the project. The function of this file in this course is mainly to make the project fully support C++11 features. You only need to add the following paragraph to the file talk.

QMAKE_CXXFLAGS += -std=c++11

2. From C to C++

In addition to object-oriented content, C++ also has some differences from C language. This chapter mainly explains some differences that have nothing to do with object-oriented.

1. Quote reference (emphasis)

1.1 Concept

A reference is an "alias" for a variable, and operations on the reference are exactly the same as operations on the variable itself.

#include 

using namespace std;


int main()
{
       
       
    int a = 1;
    // 创建一个变量a的引用
    int& b = a; // 创建一个a的引用b(b引用a)
    // a和b的操作完全相同
    b++;
    a++;
    cout << a << " " << &<< endl; // 3 0x61fe88
    cout << b << " " << &<< endl; // 3 0x61fe88

    return 0;
}

1.2 Nature

(1) You can change the value of the referenced variable, but it cannot become a reference to other variables again .

#include 

using namespace std;


int main()
{
       
       
    int a = 1;
    int b = 2;
    int& c = a; // c是a的引用
    c = b; // 把b的值赋给c
    cout << a << " " << &<< endl; // 2 0x61fe88
    cout << b << " " << &<< endl; // 2 0x61fe84
    cout << c << " " << &<< endl; // 2 0x61fe88
//    int& c = b; 错误
//    &c = b; 错误

    return 0;
}

(2) When declaring a reference, it must be initialized.

#include 

using namespace std;


int main()
{
       
       
    int a = 1;
//    int& b; 错误
    b = a;

    return 0;
}

(3) The value initialized when declaring a reference cannot be NULL.

#include 

using namespace std;


int main()
{
       
       
//    int& a = NULL; 错误

    return 0;
}

(4) If the initial value of the declared reference is a pure value, you need to use the const keyword to modify the reference, indicating that the value of the reference is immutable (constant reference, constant reference).

#include 

using namespace std;


int main()
{
       
       
    // 常引用
    const int &= 123;
//    a++; 错误
    cout << a << endl; // 123

    return 0;
}

(5) You can assign the address of the variable reference to a pointer (the pointer points to the reference), and at this time the pointer still points to the original variable.

#include 

using namespace std;


int main()
{
       
       
    int a = 1;
    int &= a;
    int* c = &b; // 指针c指向b
    a++;
    cout << *<< endl; // 2

    return 0;
}

(6) You can create references to pointers

#include 

using namespace std;


int main()
{
       
       
    int a = 1;
    int* b = &a; // b是a的指针
    int*& c = b; // c是b的引用
    cout << &<< " " << a << endl; // 0x61fe88 1
    cout << b << " " << *<< endl; // 0x61fe88 1
    cout << c << " " << *<< endl; // 0x61fe88 1

    return 0;
}

(7) The const keyword can be used to modify the reference. At this time, the value cannot be modified by reference, but the original variable value of the reference can be modified.

#include 

using namespace std;


int main()
{
       
       
    int a = 1;
    const int &= a; // b是a的常引用
//    b++; 错误
    a++;
    cout << b << endl; // 2

    return 0;
}

1.3 Reference parameters

The parameters of a function can be defined as reference types, which can improve the efficiency of parameter passing, because references, like pointers, will generate copies.

Reference parameters should be defined as const if they can be defined as const, so as to achieve reference safety.

#include 

using namespace std;


void show(const int& a,const int& b)
{
       
       
    cout << a << b << endl;
}

int main()
{
       
       
    int a = 1;
    int b = 2;
    show(a,b); // 12

    return 0;
}

[Example] Write a function, input two integer parameters, and require the function to exchange the values ​​of the two input parameters.

You can use the XOR operation to exchange two values. The principle is that using the same number to perform two consecutive XOR operations on the same number can restore the original value.

#include 

using namespace std;

/**
 * @brief swap1 错误,交换的是swap1局部的变量
 */
void swap1(int a,int b)
{
       
       
    // 异或交换
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

/**
 * @brief swap2 正确,但复杂
 */
void swap2(int* a,int* b)
{
       
       
    *= *^ *b;
    *= *^ *b;
    *= *^ *b;
}

/**
 * @brief swap3 正确且简单
 */
void swap3(int& a,int& b)
{
       
       
    a = a ^ b;
    b = a ^ b;
    a = a ^ b;
}

int main()
{
       
       
    int a = 1;
    int b = 2;
    swap1(a,b);
    cout << a << endl; // 1
    cout << b << endl; // 2
    swap2(&a,&b);
    cout << a << endl; // 2
    cout << b << endl; // 1
    swap3(a,b);
    cout << a << endl; // 1
    cout << b << endl; // 2

    return 0;
}

2. Assignment (familiarity)

In order to be consistent with the operation method of the following objects, a new assignment method is also added to ordinary variables in C++.

#include 

using namespace std;


int main()
{
       
       
    int a = 1;
    // C++支持以下写法
    int b(2); // 相当于 int b = 2;
    cout << a << " " << b << endl; // 1 2
    int c(a); // 相当于 int c = a
    cout << c << endl; // 1

    b = 4;
    cout << b << endl; // 4
//    b(5); 错误

    return 0;
}

Added data narrowing support in C++11.

#include 

using namespace std;


int main()
{
       
       
    double a = 3.14;
    int b = a;
    int c(a);
    cout << b << " " << c << endl; // 3 3
    // C++11窄化警告
    int d{
       
       a};
    cout << d << endl; // 3

    return 0;
}

3. Input (familiarity)

In C++, cin is used to record keyboard input. Like cout, cin also supports continuous input.

#include 

using namespace std;


int main()
{
       
       
    cout << "请输入两个数字:" << endl;
    int a;
    int b;
    cin >> a >> b; // 连续输入
    cout << "您输入的数字是:" << a << " " << b << endl;
    // C++中使用string类型作为字符串类型
    string c;
    cout << "请输入一个字符串:" << endl;
    cin >> c;
    cout << "您输入的字符串是:" << c << endl;

    return 0;
}

4. String type string (master)

4.1 Basic use

The string type is not a basic type of C++. String is a class in the C++ standard library. When using it, you need to introduce the header file #include instead of #include.

The string type can replace char* in most cases, and there is no need to worry about memory, length and other issues. At the same time, the string class contains many built-in functions to complete specific operations.

#include 

using namespace std;


int main()
{
       
       
    string s = "你好";
    // 不支持中文(使用的ASCII编码)
    cout << "s的长度:" <<  s.size() << endl;
    cout << "s[0]=" << s[0] << endl;
    s = "Hello";
    cout << "s的长度:" <<  s.size() << endl;
    cout << "s[0]=" << s[0] << endl;

    return 0;
}

4.2 Traversal

String supports three kinds of traversal: subscript loop, for-each, iterator (explained in the exclusive chapter later)

#include 

using namespace std;


int main()
{
       
       
    string s = "hello bye!";

    cout << "-----------下标遍历----------" << endl;
    for(int i = 0;i<s.length();i++) //length函数与size函数完全一样
    {
       
       
        cout << s[i] << endl;
    }

    cout << "-----------C++ 11 for each--------------" << endl;
    for(char i:s) // 脱离下标
    {
       
       
        cout << i << endl;
    }

    // 迭代器:略

    return 0;
}

4.3 at function

You can use the at function to extract a single character. Compared with [], the at function is more recommended because the at function is safer.

#include 

using namespace std;


int main()
{
       
       
    string s = "hello bye!";

    // 取出第三个字符
    cout << s[2] << endl; // l
    cout << s.at(2) << endl; // l

    // 尝试取出第100个字符
//    cout << s[99] << endl; // '\0'
    cout << s.at(99) << endl; // 程序终止

    cout << "主函数执行完毕!" << endl;
    return 0;
}

5. Shortcut keys

button

Function

Alt + 0 

Show/Hide Sidebar

First Ctrl+A to select the range, then Ctrl+I to align

Align code within selected range

Ctrl + R

run

Ctrl + /

single line comment

6. Functions

6.1 Inline functions (mastering)

The inline function in C++ can replace the macro-defined function in C. The inline function allows a function to directly expand the function body into the main function for compilation during the compilation phase, so it can improve the execution efficiency of the program and eliminate the overhead of ordinary function calls. , that is, ordinary functions will be expanded during the running of the program.

Usually some short (1-5 lines, no complex control statements) and frequently called functions are set as inline functions.

#include 

using namespace std;

void test1()
{
       
       
    cout << "这是一个普通的函数" << endl;
}

inline void test2()
{
       
       
    cout << "这是一个内联函数" << endl;
}

// 先声明后定义
void test3();

void test3()
{
       
       
    cout << "这是一个普通的函数" << endl;
}

void test4();

inline void test4() // 如果声明与定义分离,inline要在函数定义处使用
{
       
       
    cout << "这是一个内联函数" << endl;
}


int main()
{
       
       
    test1();
    test2();
    test3();
    test4();

    return 0;
}

Member functions of a class are by default inline functions.

6.2 Function overloading overload (emphasis)

C++ allows one function name to define multiple functions, but the parameters of these functions are different (number or type), which has nothing to do with the return value.

#include 

using namespace std;

void print()
{
       
       
    cout << "没有参数的print函数" << endl;
}

//int print() 错误
//{
       
       
//    cout << "没有参数的print函数" << endl;
//}

void print(int a)
{
       
       
    cout << "一个int参数的print函数"<< a << endl;
}

void print(string s)
{
       
       
    cout << "一个string参数的print函数" << s << endl;
}

void print(int a,int b)
{
       
       
    cout << "两个int参数的print函数" << a << b << endl;
}


int main()
{
       
       
    // 通过传入参数的不同,可以调用不同的重载函数
    print(1);
    print(1,2);
    print();
    print("hjahhah");

    return 0;
}

In addition to the above ordinary functions that can be overloaded, the constructors and member functions learned later can also be overloaded, but the destructor cannot be overloaded.

6.3 Default parameters of functions (mastering)

C++ allows default values ​​(default values) to be set for parameters of functions, and default values ​​will be used when calling without passing parameters. If the function declaration is separated from the definition, the default value of the parameter of the function can be written at the declaration or definition, but it can only appear once.

The default value follows the rightward principle (backward principle): when a parameter in a function is set to a default value, all parameters on the right must be set to a default value.

#include 

using namespace std;

void show(int a = 1,int b = 2,int c = 3)
{
       
       
    cout << a << " " << b << " " << c << endl;
}

void print(int a,int b);

void print(int a=1,int b =1)
{
       
       
    cout << a << " " << b << endl;
}

void print2(int a = 1);

void print2(int a)
{
       
       
    cout << a << endl;
}


int main()
{
       
       
    show(); // 1 2 3
    show(6); // 6 2 3
    show(5,6); // 5 6 3
    show(6,7,8); // 6 7 8

    print(); // 1 1
    print(2); // 2 1
    print(2,3); // 2 3

    print2(); // 1
    print2(2); // 2

    return 0;
}

Function parameter default values ​​and function overloading can theoretically be used together, but be careful not to conflict (ambiguity).

#include 

using namespace std;

void show(int a = 1,int b = 2,int c = 3)
{
       
       
    cout << a << " " << b << " " << c << endl;
}

void show()
{
       
       
    cout << "哈哈哈哈哈" << endl;
}


int main()
{
       
       
//    show(); 错误:二义性

    return 0;
}

6.4 Dummy functions (to master)

A function whose parameter has only type but no name is called a dummy parameter, and such a function is a dummy function.

The use of dummy functions is as follows:

  • Can suppress the warning if the parameter is not used
  • Keep functions forward-compatible
  • distinguish overloaded functions (operator overloading)
#include 

using namespace std;

void print(string) // 哑元函数
{
           
           
    cout << "AAA" << endl;
}

void print(int); // 哑元函数

void print(int)
{
           
           
    cout << "BBB" << endl;
}

void test(bool); // 声明的时候省略了参数名称

void test(bool b) // 不用的话会打警告
{
           
           
    cout << "CCC" << endl;
}

int main()
{
           
           
//    print(); 错误
    print("你好");
    print(123);
    print(true);

    return 0;
}

Exercise 1: Input a number between 100-999 on the keyboard, and output the tens and hundreds of digits of this number in sequence.

#include <iostream>
#include <string>
using namespace std;

int main()
{
    int a;
    cout << "please input 100-999" << endl;
    cin>>a;
    cout << "single digits is" << a%10<<endl;
    cout << "ten digits is" << a/10%10<<endl;
    cout << "hundred digits is" << a/100<<endl;

    return 0;
}

Exercise 2: Enter a line of strings, and count the number of English letters, numbers and other characters in it.

#include <iostream>

using namespace std;

int main()
{
    string s;
    cout << "please input string" << endl;
    cin>>s;
    int digit=0;
    int letter=0;
    int other=0;
for(int i=0;i<s.size();i++)
    {
        if(s.at(i)>='0'&&s.at(i)<='9')
        {
            digit++;
        }
        else if((s.at(i)>='a'&&s.at(i)<='z')||(s.at(i)>='A'&&s.at(i)<='Z'))
        {
            letter++;
        }
        else
        {
          other++;
        }
    }
     cout << "digit number is" <<digit<< endl;
     cout << "letter number is" <<letter<< endl;
     cout << "other number is" <<other<< endl;
    return 0;

}

3. Object-oriented basis

1. The concept of class and object (master)

A class is an abstract summary of the same kind of things, and it is a concept.

Objects are independent entities created according to the rules of a class.

You must first write the code of the class before you can create this class object. All objects in the code have corresponding classes.

The main content in a class includes, this section mainly discusses members:

2. Class content (mastery)

The most basic content of a class includes attributes and behaviors. Attributes represent the data stored inside the class object, and behaviors represent the actions that the class object can perform, usually in the form of functions.

【Example】Take the mobile phone entity as an example to illustrate the class definition.

Attributes of the phone: brand, model, weight

Mobile phone behavior: listening to music, playing games, making calls

#include 

using namespace std;

/**
 * @brief 所有类名都要使用帕斯卡(大驼峰)命名法,即所有单词的首字母使用大写
 */
class MobilePhone
{
         
         
public: // 公有权限:无任何访问限制
    string brand;
    string model;
    int weight;

    void play_music() // 播放音乐
    {
         
         
        cout << "临时抱佛脚!" << endl;
    }

    void run_game() // 运行游戏
    {
         
         
        cout << "原神!" << endl;
    }

    void communicate() // 通讯
    {
         
         
        cout << "喂?" << endl;
    }
};

The above is the most basic class, and this completely open class is very much like a structure. That's not all there is to a class.

3. Create objects (emphasis)

After a class is written, entity objects need to be created to perform actions to complete specific functions.

Objects are created in two ways:

  • stack memory object

The creation of the stack memory object does not require any keywords, and it will be automatically destroyed after its life cycle (where {}) ends.

#include 

using namespace std;

/**
 * @brief 所有类名都要使用帕斯卡(大驼峰)命名法,即所有单词的首字母使用大写
 */
class MobilePhone
{
         
         
public: // 公有权限:无任何访问限制
    string brand;
    string model;
    int weight;

    void play_music() // 播放音乐
    {
         
         
        cout << "临时抱佛脚!" << endl;
    }

    void run_game() // 运行游戏
    {
         
         
        cout << "原神!" << endl;
    }

    void communicate() // 通讯
    {
         
         
        cout << "喂?" << endl;
    }
};

int main()
{
         
         
    // 栈内存对象
    MobilePhone mp1;
    // 给属性赋值
    mp1.brand = "苹果";
    mp1.model = "15 Pro Max";
    mp1.weight = 200;
    // 调用属性值
    cout << mp1.brand << endl;
    cout << mp1.model << endl;
    cout << mp1.weight << endl;
    // 调用成员函数
    mp1.run_game();
    mp1.communicate();
    mp1.play_music();

    return 0;
}

  • heap memory object

Heap memory objects need to be created using the new keyword and destroyed using the delete keyword. If not manually destroyed, the object will persist. Heap memory objects need to be accessed using pointers.

#include 

using namespace std;

/**
 * @brief 所有类名都要使用帕斯卡(大驼峰)命名法,即所有单词的首字母使用大写
 */
class MobilePhone
{
         
         
public: // 公有权限:无任何访问限制
    string brand;
    string model;
    int weight;

    void play_music() // 播放音乐
    {
         
         
        cout << "临时抱佛脚!" << endl;
    }

    void run_game() // 运行游戏
    {
         
         
        cout << "原神!" << endl;
    }

    void communicate() // 通讯
    {
         
         
        cout << "喂?" << endl;
    }
};

int main()
{
         
         
    MobilePhone* mp2 = new MobilePhone; // 创建堆内存对象
    // 给属性赋值
    mp2->brand = "小米";
    mp2->model = "13 Pro Ultra";
    mp2->weight = 199;
    // 获取属性值
    cout << mp2->brand << endl;
    cout << mp2->model << endl;
    cout << mp2->weight << endl;
    // 调用成员函数
    mp2->play_music();
    mp2->run_game();
    mp2->communicate();
    // 手动销毁对象
    delete mp2;
//    cout << mp2->brand << endl; 错误,销毁后不得使用
    mp2 = NULL; // 更稳妥的方式,防止销毁后使用

    return 0;
}

4. Packaging (emphasis)

The classes in Section 3 look similar to structs, because the permissions of the classes are completely open. Encapsulation is one of the characteristics of object-oriented programming, which requires a class to hide its attributes and implementation details, and re-provide the interface that needs to be called to the outside.

You can use private and other permissions to hide the members of the class, and use public to re-open the access interface as needed.

#include 

using namespace std;

/**
 * @brief 所有类名都要使用帕斯卡(大驼峰)命名法,即所有单词的首字母使用大写
 */
class MobilePhone
{
         
         
private: // 私有权限:只能在类内部访问
    string brand; // 可读可写
    string model; // 只写
    int weight = 188; // 只读,赋予了初始值

public:
    string get_brand() // 读函数:getter
    {
         
         
        return brand;
    }

    void set_brand(string b) // 写函数:setter
    {
         
         
        brand = b;
    }

    void set_model(string m) // setter
    {
         
         
        model = m;
    }

    int get_weight() // getter
    {
         
         
        return weight;
    }
};

int main()
{
         
         
    MobilePhone mp1;
    mp1.set_brand("华为");
    mp1.set_model("P60");
    cout << mp1.get_brand() << endl;
    cout << mp1.get_weight() << endl;

    MobilePhone* mp2 = new MobilePhone;
    mp2->set_brand("魅族");
    mp2->set_model("20");
    cout << mp2->get_brand() << endl;
    cout << mp2->get_weight() << endl;
    delete mp2;

    return 0;
}

5. Constructor (emphasis)

5.1 Concept

The constructor is used to create a class object. When the constructor is not written before, the compilation will automatically add a default constructor.

Features of the constructed function are:

  • The function name is exactly the same as the class name
  • Constructor does not write return value
  • Constructor supports function overloading

The following is a constructor automatically added by the compiler. This constructor has no parameters, the function body is empty and the permissions are public. As long as the programmer writes any constructor by hand, the compiler will no longer automatically add a default constructor.

#include 

using namespace std;


class MobilePhone
{
         
         
private:
    string brand;
    string model;
    int weight = 188;

public:
    MobilePhone()
    {
         
         
        cout << "生产了一部手机" << endl;
    }

    string get_brand()
    {
         
         
        return brand;
    }

    void set_brand(string b)
    {
         
         
        brand = b;
    }

    void set_model(string m)
    {
         
         
        model = m;
    }

    int get_weight()
    {
         
         
        return weight;
    }
};

int main()
{
         
         
    MobilePhone mp1; // 生产了一部手机
    MobilePhone* mp2 = new MobilePhone; // 生产了一部手机

    delete mp2;

    return 0;
}

Constructors are often used to assign initial values ​​to properties of newly created objects.

#include 

using namespace std;


class MobilePhone
{
         
         
private:
    string brand;
    string model;
    int weight;

public:
    MobilePhone(string b,string m,int w)
    {
         
         
        cout << "生产了一部手机" << endl;
        // 赋予初始值
        brand = b;
        model = m;
        weight = w;
    }

    void show()
    {
         
         
        cout << brand << endl;
        cout << model << endl;
        cout << weight << endl;
    }
};

int main()
{
         
         
    MobilePhone mp1("Apple","14",288); // 生产了一部手机
    mp1.show();
    MobilePhone* mp2 = new MobilePhone("一加","10",199); // 生产了一部手机
    mp2->show();
    delete mp2;

    return 0;
}

5.2 Overloading

Constructors support overloading and default values ​​for parameters.

#include 

using namespace std;


class MobilePhone
{
         
         
private:
    string brand;
    string model;
    int weight;

public:
    MobilePhone(string b,string m,int w)
    {
         
         
        cout << "生产了一部手机" << endl;
        // 赋予初始值
        brand = b;
        model = m;
        weight = w;
    }

    MobilePhone() // 构造函数重载
    {
         
         
        cout << "生产了一部手机" << endl;
        brand = "8848";
        model = "M6巅峰版";
        weight = 500;
    }

    void show()
    {
         
         
        cout << brand << endl;
        cout << model << endl;
        cout << weight << endl;
    }
};

int main()
{
         
         
    MobilePhone mp1("Apple","14",288); // 生产了一部手机
    mp1.show();
    MobilePhone* mp2 = new MobilePhone("一加","10",199); // 生产了一部手机
    mp2->show();
    delete mp2;

    // 也可以使用无参构造函数创建对象
    MobilePhone mp3;
    mp3.show();
    MobilePhone* mp4 = new MobilePhone;
    mp4->show();
    delete mp4;

    return 0;
}

5.3 Constructing an initialization list

This is a simpler way of writing.

#include 

using namespace std;


class MobilePhone
{
         
         
private:
    string brand;
    string model;
    int weight;

public:
    MobilePhone(string b,string m,int w)
        :brand(b),model(m),weight(w) // 构造初始化列表
    {
         
         
        cout << "生产了一部手机" << endl;
    }

    // 构造初始化列表
    MobilePhone():brand("8848"),model("M6巅峰版"),weight(500)
    {
         
         
        cout << "生产了一部手机" << endl;
    }

    void show()
    {
         
         
        cout << brand << endl;
        cout << model << endl;
        cout << weight << endl;
    }
};

int main()
{
         
         
    MobilePhone mp1("Apple","14",288); // 生产了一部手机
    mp1.show();
    MobilePhone* mp2 = new MobilePhone("一加","10",199); // 生产了一部手机
    mp2->show();
    delete mp2;

    // 也可以使用无参构造函数创建对象
    MobilePhone mp3;
    mp3.show();
    MobilePhone* mp4 = new MobilePhone;
    mp4->show();
    delete mp4;

    return 0;
}

A constructor initializer list has the following characteristics:

  • Constructing an initialization list performs more efficiently
  • If it does not affect the operation of the code, you can choose whether to use it according to the situation
  • If the member variable is modified by const, it cannot be assigned in {} of the constructor, but can be assigned in the constructor initialization list.
  • Can distinguish between member variables and local variables with the same name

5.4 Copy constructor

5.4.1 The default copy constructor

If the programmer does not write a copy constructor, the compiler will provide each class with an overloaded copy constructor for object copying, that is, creating an object with exactly the same data based on an object. It should be noted that the newly created object and the original object are two objects.

#include 

using namespace std;


class MobilePhone
{
         
         
private:
    string brand;
    string model;
    int weight;

public:
    MobilePhone(string b,string m,int w)
        :brand(b),model(m),weight(w)
    {
         
         
        cout << "带参数的构造函数" << endl;
    }

    // 默认添加的拷贝构造函数
    MobilePhone(const MobilePhone& m)
    {
         
         
        cout << "拷贝构造函数" << endl; // 默认的拷贝构造函数去掉这句话
        brand = m.brand;
        model = m.model;
        weight = m.weight;
    }

    void show()
    {
         
         
        cout << brand << endl;
        cout << model << endl;
        cout << weight << endl;
    }
};

int main()
{
         
         
    MobilePhone mp1("oppo","Find X6 Pro",201);
    // 调用默认的拷贝构造函数
    MobilePhone mp2(mp1);
    mp1.show();
    mp2.show();
    cout << &mp1 << " " << &mp2 << endl;

    return 0;
}

5.4.2 Shallow copy

When a member variable of pointer type appears in the class, the default copy constructor will cause the problem of shallow copy. Member variables of different objects will point to the same area, which does not conform to object-oriented design.

#include 
#include 

using namespace std;

class Dog
{
         
         
private:
    char* name;

public:
    Dog(char* n)
    {
         
         
        name = n;
    }

    // 复原浅拷贝(写不写都行)
    Dog(const Dog& d)
    {
         
         
        name = d.name;
    }

    void show()
    {
         
         
        cout << name << endl;
    }
};


int main()
{
         
         
    char c[20] = "wangcai";

    Dog d1(c);
    // 拷贝构造函数
    Dog d2(d1);

    strcpy(c,"xiaobai");

    d1.show(); // xiaobai
    d2.show(); // xiaobai


    return 0;
}

5.4.3 Deep Copy

#include 
#include 

using namespace std;

class Dog
{
         
         
private:
    char* name;

public:
    Dog(char* n)
    {
         
         
        // 单独开辟一块堆内存
        name = new char[20];
        strcpy(name,n);
    }

    // 深拷贝
    Dog(const Dog& d)
    {
         
         
        // 单独开辟一块堆内存
        name = new char[20];
        strcpy(name,d.name);
    }

    void show()
    {
         
         
        cout << name << endl;
    }
};


int main()
{
         
         
    char c[20] = "wangcai";

    Dog d1(c);
    // 拷贝构造函数
    Dog d2(d1);

    strcpy(c,"xiaobai");

    d1.show(); // wangcai
    d2.show(); // wangcai


    return 0;
}

Although the above deep copy code solves the problem of shallow copy, it also causes the heap memory space created by new to be unable to be released, resulting in memory leaks.

5.5 Implicitly calling constructors

#include 

using namespace std;

class Teacher
{
         
         
private:
    string name;

public:
    // 使用explicit修饰构造函数后,只支持显式调用
    Teacher(string n)
    {
         
         
        cout << "创建了一个老师" << endl;
        name = n;
    }

    string get_name()
    {
         
         
        return name;
    }
};

int main()
{
         
         
    string name = "罗翔";
    // 隐式调用构造函数
    Teacher t = name; // 创建了一个老师对象
    cout << t.get_name() << endl; // 罗翔

    return 0;
}

6. Destructor (emphasis)

A destructor is the exact opposite of a constructor in a class.

Constructor

destructor

Create an object after calling manually

Called automatically when the object is destroyed

Can have parameters, support overloading and parameter default values

no parameters

The function name is the class name

function name is ~ class name

Usually used for object creation initialization

Usually used to reclaim memory and resources when objects are destroyed

The destructor has no return value.

#include 

using namespace std;

class Test
{
         
         
public:
    ~Test()
    {
         
         
        cout << "析构函数" << endl;
    }
};


int main()
{
         
         
    { // 局部代码块
        Test t1;
    }

    Test* t2 = new Test;
    delete t2;

    cout << "主函数执行完毕" << endl;
    return 0;
}

Now go back to the deep copy code in section 5.4.3, you only need to release the allocated memory space in the destructor.

#include 
#include 

using namespace std;

class Dog
{
         
         
private:
    char* name;

public:
    Dog(char* n)
    {
         
         
        // 单独开辟一块堆内存
        name = new char[20];
        strcpy(name,n);
    }

    // 复原浅拷贝(写不写都行)
    Dog(const Dog& d)
    {
         
         
        // 单独开辟一块堆内存
        name = new char[20];
        strcpy(name,d.name);
    }

    void show()
    {
         
         
        cout << name << endl;
    }

    // 析构函数
    ~Dog()
    {
         
         
        delete name;
    }
};


int main()
{
         
         
    char c[20] = "wangcai";

    Dog d1(c);
    // 拷贝构造函数
    Dog d2(d1);

    strcpy(c,"xiaobai");

    d1.show(); // wangcai
    d2.show(); // wangcai

    return 0;
}

7. Scope qualifiers ::

7.1 Namespaces (Mastery)

The name space is a subdivided level of code to prevent the occurrence of duplicate names. In addition, the namespace can also play a role in classifying the code. The most common namespace is the standard namespace std that comes with the C++ source code.

#include 

// 使用标准名字空间
using namespace std;

int a = 1;

// 自定义名字空间
namespace my_space {
         
         
    int a = 3;
    int b = 4;
}

// 使用名字空间
using namespace my_space;

int main()
{
         
         
    std::string text = "Hello";
    std::cout << text << std::endl;

    int a = 2;
    cout << a << endl; // 2
    // 匿名名字空间
    cout << ::a << endl; // 1
    cout << my_space::a << endl; // 3

    // 不使用作用域限定符访问的前提是没有冲突
    cout << b << endl; // 4

    return 0;
}

7.2 In-class declaration, out-of-class definition (emphasis)

Members in a class can also be declared and defined separately. In this case, they need to be declared within the class and can be defined outside the class.

#include 

using namespace std;

class Student
{
         
         
private:
    string name = "张三";
public:
    // 声明
    void study();
};

// 定义
void Student::study()
{
         
         
    cout << name << "在努力学习!" << endl;
}

int main()
{
         
         
    Student s;
    s.study(); // 张三在努力学习!

    return 0;
}

8. this pointer

8.1 Concept (emphasis)

The this pointer is a special pointer that can only be used in member functions, constructors, and destructors of a class. The this pointer points to the first address of the object of the current class.

#include 

using namespace std;

class Test
{
         
         
public:
    Test()
    {
         
         
        cout << this << endl;
    }
};

int main()
{
         
         
    Test t1; // 0x61fe7b
    cout << &t1 << endl; // 0x61fe7b
    Test* t2 = new Test; // 0x8f0fe0
    cout << t2 << endl; // 0x8f0fe0
    delete t2;

    return 0;
}

8.2 Distinguish between local variables and member variables with the same name (master)

#include 

using namespace std;

class Computer
{
         
         
private:
    string brand;

public:
    Computer(string brand)
    {
         
         
        // 区分重名
        this->brand = brand;
    }

    string get_brand()
    {
         
         
        // 所有的成员在类内部调用都是靠this指针,平常可省略
        return this->brand;
    }

    void show()
    {
         
         
        // 所有的成员在类内部调用都是靠this指针,平常可省略
        cout << this->get_brand() << endl;
    }
};

int main()
{
         
         
    Computer c("联想");
    cout << c.get_brand() << endl; // 联想
    c.show(); // 联想


    return 0;
}

8.3 Chaining calls (mastery)

This points to the first address of the class object, and *this represents the object itself. This usage is usually used in combination with the current class reference as the return value.

When the return value of a member function of a class is a reference to this class, it means that this function supports chain calls and can be called continuously, like a chain.

#include 

using namespace std;

class Number
{
         
         
private:
    int value;

public:
    Number(int value):value(value) // 重名时使用构造初始化列表也可以
    {}

    Number& add(int v) // 支持链式调用
    {
         
         
        value += v;
        return *this; // 固定用法
    }

    int get_value()
    {
         
         
        return value;
    }
};


int main()
{
         
         
    Number n1(1);
    // 传统方式:非链式调用
    n1.add(2);
    n1.add(3);
    n1.add(4);
    cout << n1.get_value() << endl; // 10

    Number n2(1);
    // 链式调用
    cout << n2.add(2).add(3).add(4).get_value() << endl; // 10

    // string类的append函数在源代码中也采用了链式调用的设计
    string s = "A";
    cout << s.append("B").append("C").append("D") << endl; // ABCD

    return 0;
}

In actual development, this pointer is often used in conjunction with polymorphism .

Exercise: The cat eats the fish problem.

Write a class Fish , which has two attributes of species and weight. The type of attribute can be chosen by oneself , and attribute encapsulation is required.

Write a class Cat , which has a public member function:

Fish& eat(Fish &f);

The function of the eat function requires judging the species of Fish :

  • If the species is "Saury", then output "No matter how heavy it is, I love to eat it.". At the same time modify the weight of Fish &f to 0 , and return it as the return value of the function.
  • If the species is not "Saury", judge the weight of the fish. If the weight is greater than 200 , eat the fish and output the information and return a Fish with a weight of 0 ; if the weight is less than 200 , output the information, do not modify the weight of the fish, and return the fish directly object.

Other knowledge points (constructor, construction initialization list, etc.) can choose whether to adopt it.

#include <iostream>

using namespace std;
class Fish
{
private:
    string variety;
    float weight;
    
public:
    Fish(string v,float w):variety(v),weight(w){}
    
    string get_variety()
    {
        return variety;
    }
    
    void set_weight(float w)
    {
        weight=w;
    }
    
    float get_weight()
    {
        return weight;
    }
    
};

class  Cat
{
public:
    Fish& eat(Fish &f)
    {
        if(f.get_variety()=="秋刀鱼")
        {
            cout<<"无论多沉,我都爱吃。"<<endl;
            f.set_weight(0);
        }
        else
        {
            if(f.get_weight()<200)
            {
                cout<<"不爱吃。"<<endl;
            }
            else
            {
                cout<<"大鱼我也爱吃。"<<endl;
                f.set_weight(0);
            }
        }
        return f;
    }
    
};



int main()
{
    
    Fish f1("秋刀鱼",1231);
    
    Fish f2("娃娃鱼",1231);
    Fish f3("马哈鱼",123);
    
    Cat cat;
    cat.eat(f1);
    cout<<f1.get_weight()<<endl;
    
    cat.eat(f2);
    cout<<f2.get_weight()<<endl;
    
    cat.eat(f3);
    cout<<f3.get_weight()<<endl;
    return 0;
}

9. static keyword

This chapter mainly discusses the usage of static in classes: static member variables and static member functions.

9.1 Static member variables (master)

Ordinary member variables are static member variables with static modification. This static member variable needs to be declared within the class and initialized outside the class.

The difference between static member variables and non-static member variables are:

  • All objects of this class have a common copy of static member variables, and each object of this class has a copy of non-static member variables.
  • Static members (static member variables and static member functions) can be called directly using the class name. Try to use this method because it can improve the readability of the code.
  • Static member variables open up memory when the program starts running, and automatically reclaim the memory when the program ends.
  • This pointer can call static members (static member variables and static member functions).

#include 

using namespace std;

class Test
{
         
         
public:
    string str1 = "非静态成员变量";
    static string str2; // 类内只声明
    static const int a = 1; // 【特例const修饰的静态成员变量可以类内初始化

    void function() // 非静态的成员函数
    {
         
         
        // 为了方便理解,加入this指针,实际编写的过程中可取消
        cout << this->str1 << endl;
        cout << this->str2 << endl;
    }
};

// 类外初始化
string Test::str2 = "静态成员变量";

int main()
{
         
         
    // 直接使用类名调用
    cout << Test::str2 << " " << &Test::str2 << endl; // 静态成员变量 0x40b038

    Test t1;
    cout << t1.str1 << " " << &t1.str1 << endl; // 非静态成员变量 0x61fe8c
    cout << t1.str2 << " " << &t1.str2<< endl; // 静态成员变量 0x40b038

    Test t2;
    cout << t2.str1 << " " << &t2.str1<< endl; // 非静态成员变量 0x61fe88
    cout << t2.str2 << " " << &t2.str2<< endl; // 静态成员变量 0x40b038

    t1.function(); // 非静态成员变量\n静态成员变量

    return 0;
}

9.2 Static member functions (mastering)

Static member functions do not have this pointer, so they cannot call non-static members (non-static member variables and member functions).

#include 

using namespace std;

class Test
{
         
         
private:
    string str1 = "非静态成员";
    static string str2;

public:
    void function1()
    {
         
         
        cout << "这是一个非静态成员函数:";
        cout << str1;
        cout << str2;
        cout << endl;
    }

    static void function2()
    {
         
         
        cout << "这是一个静态成员函数:";
//        cout << str1; 错误
        cout << str2;
        cout << endl;
    }
};

string Test::str2 = "静态成员";

int main()
{
         
         
    Test::function2();

    Test t;
    t.function1();
    t.function2(); // 也能通过对象,虽然不建议

    return 0;
}

9.3 Static local variables (familiarity)

A local variable modified with static is a static local variable. When the code block where the static local variable is located is called for the first time, the memory of the static local variable is opened. Unlike the non-static local variable, the static local variable will not be destroyed after the code block is called. The next time the code is called, Continue to use the previous static local variables, which will not be automatically destroyed until the program execution terminates.

#include 

using namespace std;

class Test
{
         
         
public:
    void func1()
    {
         
         
        int a = 1; // 非静态局部变量
        cout << a++ << endl;
    }

    void func2()
    {
         
         
        static int a = 1; // 静态局部变量
        cout << a++ << endl;
    }
};


int main()
{
         
         
    Test t1;
    Test t2;

    t1.func1(); // 1
    t1.func1(); // 1
    t2.func1(); // 1

    cout << "---------------" << endl;

    t1.func2(); // 1
    t1.func2(); // 2
    t2.func2(); // 3

    return 0;
}

Static variables are best not used blindly in large numbers:

  • The life cycle is too long and takes up a lot of memory.
  • Does not meet the characteristics of object-oriented.

10. The const keyword (to master)

10.1 const modified member function

Member functions modified by const are called constant member functions.

The characteristic of a constant member function is that it cannot modify the attribute value, and cannot call a non-const modified member function.

#include 

using namespace std;

class Car
{
         
         
private:
    string brand;

public:
    Car(string brand):brand(brand){}

    string get_brand() const
    {
         
         
//        set_brand("奔驰"); 错误
//        brand = "宝马"; 错误
//        show(); 错误
        show2();
        return brand;
    }

    void set_brand(string brand)
    {
         
         
        this->brand = brand;
    }

    void show()
    {
         
         
        cout << "滴滴滴" << endl;
    }

    void show2() const
    {
         
         
        cout << "哒哒哒" << endl;
    }
};

int main()
{
         
         
    Car c("奥迪");
    c.set_brand("大众");
    cout << c.get_brand() << endl;

    return 0;
}

It is recommended that as long as the member function does not modify the attribute value, it should be written as a constant member function, such as getter.

10.2 const modified object

The const modifier object indicates that the object is a constant object.

The property values ​​of constant objects cannot be changed, and non-const member functions cannot be called.

#include 

using namespace std;

class Car
{
         
         
private:
    string brand;

public:
    string model = "这个变量仅用于举例,就不封装了";

    Car(string brand):brand(brand){}

    string get_brand() const
    {
         
         
//        set_brand("奔驰"); 错误
//        brand = "宝马"; 错误
//        show(); 错误
        show2();
        return brand;
    }

    void set_brand(string brand)
    {
         
         
        this->brand = brand;
    }

    void show()
    {
         
         
        cout << "滴滴滴" << endl;
    }

    void show2() const
    {
         
         
        cout << "哒哒哒" << endl;
    }
};


int main()
{
         
         
    // const两个位置都可以
    const Car c1("奥迪");
    Car const c2("凯迪拉克");

//    c1.set_brand("大众"); 错误
    cout << c1.get_brand() << endl;
//    c1.show(); 错误
    c1.show2();
//    c1.model = "A4"; 错误
    cout << c1.model << endl;

    return 0;
}

10.3 const modified member variables

A member variable modified by const means a constant member variable.

Once the initial value of a constant member variable is set, it cannot be changed during runtime.

#include 

using namespace std;

class Person
{
         
         
private:
    const string name; // 常成员变量

public:
    const int age = 1; // 赋予初始值方式二

    Person():name("张三"){} //赋予初始值方式一(推荐)

    // 重载的构造函数
    Person(string name,int age):name(name),age(age){}

    void set_name(string name)
    {
         
         
//        this->name = name; 错误
    }

    string get_name() const
    {
         
         
        return name;
    }
};


int main()
{
         
         
    Person p;
    cout << p.get_name() << endl;
//    p.age++; 错误
    cout << p.age << endl;

    Person p2("李四",18);
    cout << p2.age << endl; // age:18

    return 0;
}

10.4 const modified local variables

Indicates that local variables are immutable and are usually used for function parameters of reference type.

Code: slightly

See Reference Parameters section for details.

Fourth, operator overloading

1. Tomomoto

The class has encapsulation, and the hidden attribute value can be read and written only through the public interface. But friends can break through the limitation of permissions and access any part of the class at will.

Friend can improve the running efficiency of the program, but it destroys the object-oriented features, so it should be used with caution in actual development .

Friends can be divided into several usages:

  • friend function
  • friend class
  • friend member function

1.1 Friend function (emphasis) 

A friend function does not belong to a class, it is a function outside the class, but it can be "declared" within the class. Friend functions can break through the permissions of the class.

#include 

using namespace std;

class Job
{
          
          
private:
    int income;

public:
    Job(int income):income(income)
    {
          
          
        cout << &this->income << endl;
    }

    // “声明”友元函数
    friend void test_friend(Job&);
};

void test_friend(Job& j)
{
          
          
    // 尝试访问私有成员
    cout << ++j.income << " " << &j.income << endl;
}

int main()
{
          
          
    Job j1(20000); // 0x61fe8c
    test_friend(j1); // 20001 0x61fe8c

    return 0;
}

The characteristics of friend functions are as follows:

  • Friend function does not belong to any class
  • Since it does not belong to any class, the friend function has no this pointer, so when breaking through the authority, the object must be used.
  • The "declaration" of a friend function can be placed anywhere in the class, including private.
  • In theory, a friend function can be a friend function of multiple classes, and only needs to be "declared" in each class.

1.2 Friend classes (mastery)

When a class B becomes a friend class of another class A, all members of class A can be accessed by class B.

#include 

using namespace std;

class A
{
          
          
private:
    int value = 1;

public:
    int get_value() const
    {
          
          
        return value;
    }

    // “声明”友元关系
    friend class B;
};

class B
{
          
          
public:
    void test_friend(A& a)
    {
          
          
        cout << ++a.value << endl;
    }

    void test_friend2(A& a,int v)
    {
          
          
        a.value += v;
    }
};


int main()
{
          
          
    A a;
    B b;
    b.test_friend(a); // 2
    cout << a.get_value() << endl; // 2
    b.test_friend2(a,100);
    cout << a.get_value() << endl; // 102

    return 0;
}

Regarding the friend class, you need to pay attention to the following points:

  • Friendship has nothing to do with inheritance
  • The friendship relationship is one-way and not exchangeable
  • Friendship is not transitive

1.3 Friend member function (familiar)

A specific member function in class B can be called a friend member function of class A, so that only this member function of class B can access all members of class A.

#include 

using namespace std;

// 第三步:提前声明类A
class A;

// 第二步:因为友元用到了类B,补充类B和函数声明。
class B
{
          
          
public:
    void func(A& a);
};

// 第一步:确定友元的函数格式并“声明”
class A
{
          
          
private:
    string str = "这是类A私有的成员!";

    // 友元关系
    friend void B::func(A& a);
};

// 第四步:定义友元成员函数的内容
void B::func(&a)
{
          
          
    cout << a.str.append("哈哈哈") << endl;
}


int main()
{
          
          
    A a;
    B b;
    b.func(a); // 这是类A私有的成员!哈哈哈

    return 0;
}

2. Operator overloading (mastery)

In addition to functions, operators can also be overloaded, and operators can even be regarded as a function.

The operation object of the operator in C++ is usually the built-in basic type. If the object of the user-defined type also supports operator operations, then you need to use operator overloading to give new functions to the existing operators. Support new data types to complete specific operations.

Not all operators support overloading, as shown below:

Most operators support two overloading methods: friend function operator overloading and member function operator overloading.

2.1 Friend function operator overloading

#include 

using namespace std;

/**
 * @brief 自定义整数类型
 */
class Integer
{
          
          
private:
    int value;

public:
    Integer(int value):value(value){}

    int get_value() const
    {
          
          
        return value;
    }

    // “声明”友元关系
    friend Integer operator +(const Integer& i1,const Integer& i2);
    friend Integer operator ++(Integer& i); // 前置
    friend Integer operator ++(Integer& i,int); // 后置
};

Integer operator +(const Integer& i1,const Integer& i2)
{
          
          
    return i1.value + i2.value;
}

Integer operator ++(Integer& i)
{
          
          
    return ++i.value;
}

Integer operator ++(Integer& i,int)
{
          
          
    return i.value++;
}

int main()
{
          
          
    Integer i1(1);
    cout << (++i1).get_value() << endl; // 2

    Integer i2(2);
    Integer i3 = i1+i2;
    cout << (i3++).get_value() << endl; // 4
    cout << i3.get_value() << endl; // 5

    return 0;
}

2.2 Member function operator overloading

The main difference from friend function operator overloading is that member function operator overloading takes one less parameter. Because in member function operator overloading, the first operand is represented by this pointer.

#include 

using namespace std;

/**
 * @brief 自定义整数类型
 */
class Integer
{
          
          
private:
    int value;

public:
    Integer(int value):value(value){}

    int get_value() const
    {
          
          
        return value;
    }

    // 成员函数运算符重载
    Integer operator +(const Integer& i); // 类内声明
    Integer operator ++(); // 前置++
    Integer operator ++(int); // 后置++
};

// 类外定义
Integer Integer::operator +(const Integer& i)
{
          
          
    return this->value+i.value;
}

Integer Integer::operator ++()
{
          
          
    return ++this->value;
}

Integer Integer::operator ++(int)
{
          
          
    return this->value++;
}

int main()
{
          
          
    Integer i1(1);
    cout << (++i1).get_value() << endl; // 2

    Integer i2(2);
    Integer i3 = i1+i2;
    cout << (i3++).get_value() << endl; // 4
    cout << i3.get_value() << endl; // 5

    return 0;
}

2.3 Other situations

Introduces two operators that must be overloaded with member function operators.

  • assignment operator overloading
  • Type conversion operator overloading

2.3.1 Assignment operator overloading

If all classes do not write assignment operator overloading, the compiler will automatically add an assignment operator overloading function.

#include 

using namespace std;

class City
{
          
          
private:
    string name;

public:
    City(string name):name(name){}

    string get_name() const
    {
          
          
        return name;
    }

    // 以下代码写不写都一样
    City& operator =(const City& c)
    {
          
          
        cout << "赋值运算符重载" << endl; // 默认无这行代码
        this->name = c.name;
        return *this;
    }
};

int main()
{
          
          
    City c1("济南");
    City c2("青岛");
    // 赋值运算符
    cout << (c2 = c1).get_name() << endl; // 济南

    return 0;
}

As with shallow copies, the default assignment operator overloads cannot handle member variables of pointer types.

2.3.2 Type conversion operator overloading

The writing method of type conversion operator overloading is quite special.

#include 

using namespace std;

class Test
{
          
          
public:
    // 类型转换运算符重载
    operator int()
    {
          
          
        return 123;
    }
};

int main()
{
          
          
    Test t;
    int a = t; // 类型转换
    cout << a << endl;

    return 0;
}

2.4 Precautions

Operator overloading needs to pay attention to the following points:

1. Operator overloading can only be used within the scope of existing operators in C++, and new operators cannot be created.

2. Operator overloading is essentially function overloading.

3. The operator after overloading cannot change the original priority and associativity, nor can it change the operand and grammatical structure of the operator.

4. The parameters of operator overloading must contain custom data types, and the operator rules of basic types cannot be overloaded.

5. Operator overloading should conform to the original function definition as much as possible.

6. The parameters of operator overloading do not support the setting of default values.

7. In general, it is recommended that unary operators be overloaded with member functions, and binary operators be overloaded with friend functions.

Exercise: use of operators.

Customize a String class (note that it is capitalized, not the string that comes with it), and requires the following functions to be implemented

Requirement: The comparison is the length.

class String{
           
           
public:
    String(const string &s); //构造函数
    //友元运算符重载
    friend String operator +(const String &s1,const String &s2);
    friend bool operator >(const String &s1,const String &s2);
    friend bool operator <(const String &s1,const String &s2);
    friend bool operator ==(const String &s1,const String &s2);
    //成员函数运算符
    bool operator !=(const String &other);
    //赋值运算符
    String& operator =(const String &s); 
    // 类型转换
    operator string(); //String转string
    //成员函数
    string get_str();
private:
    string str;
};

accomplish:

:#include <iostream>

using namespace std;


class String{
public:
    String(const string &s); //构造函数
    //友元运算符重载
    friend String operator +(const String &s1,const String &s2);
    friend bool operator >(const String &s1,const String &s2);
    friend bool operator <(const String &s1,const String &s2);
    friend bool operator ==(const String &s1,const String &s2);
    //成员函数运算符
    bool operator !=(const String &other);
    //赋值运算符
    String& operator =(const String &s)
    {
        //    cout << "赋值运算符重载" << endl; // 默认无这行代码
        this->str = s.str;
        return *this;
    }

    //成员函数
    string get_str()
    {
        return str;

    }

    // 类型转换
    operator string()
    {
        return "hi";
    }

private:
    string str;

};

String:: String(const string &s):str(s){}


String operator +(const String &s1,const String &s2)
{
    return s1.str+s2.str;
}

bool operator >(const String &s1,const String &s2)
{
    return s1.str.size()>s2.str.size();

}


bool operator <(const String &s1,const String &s2)
{
    return s1.str.size()<s2.str.size();

}

bool operator ==(const String &s1,const String &s2)
{
    return s1.str.size()==s2.str.size();

}

bool String:: operator !=(const String &other)
{
    return this->str.size()!=other.str.size();
}



int main()
{
    String s1("he");
    String s2("llo");

    String s3=s1+s2;

    cout<<"s1="<<s1.get_str()<<" "<<"s2="<<s2.get_str()<<endl;

    bool a= s1.get_str()>s2.get_str();
    bool b= s1.get_str()<s2.get_str();
    bool c= s1.get_str()==s2.get_str();
    bool d= s1.get_str()!=s2.get_str();

    cout<<"拼接s1和s2后为:" <<s3.get_str()<<endl;
    cout<<"下面进行长度比较,分别从>、<、==比较,真为1,假为0"<<endl;
    cout<< a<<endl;
    cout<< b<<endl;
    cout<< c<<endl;
    cout<< d<<endl;
    cout<<"赋值运算:s2赋值给s1为:";
    cout << (s1 = s2).get_str() << endl;
    cout<<"类型转换:";
    string n=s1;
    cout << n << endl;

    return 0;
}

3. std::string string class

String is a class that comes with the C++ source code. A class can contain many member functions, etc. You can learn by yourself by consulting the documentation .

#include 
#include 

using namespace std;

int main()
{
              
              
    string s; // 创建一个空字符串对象
    // 判断内容是否为空
    cout << s.empty() << endl;
    string s1 = "hahaha"; // 隐式调用构造函数
    // 上面的写法相当于:
    string s2("hahaha");
    cout << "比较内容:" << (s1==s2) << endl;
    // 拷贝构造函数
    string s3(s2);
    cout << s3 << endl;

    // 参数1:char* 原字符串
    // 参数2:保留几个字符
    string s4("ABCDEF",2);
    cout << s4 << endl;

    s2 = "abcdef";
    // 参数1:string 原字符串
    // 参数2:不保留前几个字符
    string s5(s2,2);
    cout << s5 << endl;

    // 参数1:字符数量
    // 参数2:字符char内容
    string s6(5,'A');
    cout << s6 << endl;

    swap(s5,s6); // 交换内容
    cout << s5 << " " << s6 << endl;

    // 字符串连接符
    string s7 = s5+s6;
    cout << s7 << endl;

    // 向后追加字符串
    cout << s7.append("UU").append("II") << endl;
    // 向后追加单字符
    s7.push_back('O');
    cout << s7 << endl;
    // 插入字符串
    // 参数1:插入位置
    // 参数2:插入内容
    s7.insert(1,"666");
    cout << s7 << endl;

    // 删除内容
    // 参数1:起始位置
    // 参数2:删除的字符数
    s7.erase(9,6);
    cout << s7 << endl;

    // 替换内容
    // 参数1:起始位置
    // 参数2:替换的字符数
    // 参数3:新替换的内容
    s7.replace(0,3,"*********");
    cout << s7 << endl;

    // 清空
    s7.clear();
    cout << s7.length() << endl; // 0

    s7 = "1234567890";

    char b[20];
    // 参数1:拷贝到哪个字符数组
    // 参数2:拷贝的字符数量
    // 参数3:拷贝的起始位置
    // 返回值:拷贝的字符数量
    int length = s7.copy(b,3,1);
    b[length] = '\0';
    cout << b << endl;

    // C++ std::string → C string
    char c[20];
    strcpy(c,s7.c_str());
    cout << c << endl;

    // C string → C++:直接赋值
    string s8 = c;
    cout << s8 << endl;

    return 0;
}

5. Container

1. Template template

A template can declare a function or class as a generic type, and this generic type can be used inside the function or class to write type-independent code.

This style of programming is called generic programming.

This time, we will learn two ways to use templates:

  • function template
  • class template

1.1 Function templates (familiar)

Function templates are for functions with different parameter types and return value types, but the function and function name are the same.

#include 

using namespace std;

template <class T> // 声明模板的通用数据类型T
add(T a,T b)
{
                 
                 
    return a+b;
}

int main()
{
                 
                 
    // 运行时决定具体类型的计算方式
    cout << add(2,3) << endl; // 5
    cout << add(2.2,3.3) << endl; // 5.5
    string s1 = "AAA";
    string s2 = "BBB";
    cout << add(s1,s2) << endl; // AAABBB

    // const char* 不支持加法
//    cout << add("111","222") << endl; 错误

    return 0;
}

1.2 Class Templates (Mastering)

Class templates are for classes with different parameter types, return value types, and member variable types of member functions, but the algorithms for internally managing these data are the same.

#include 

using namespace std;

template <typename T> // typename可与class关键字替换
class Test
{
                 
                 
private:
    T value;

public:
    Test(T v):value(v){}

    T get_value() const
    {
                 
                 
        return value;
    }
};

class Projector // 投影仪
{
                 
                 
public:
    void show()
    {
                 
                 
        cout << "投影仪播放内容中..." << endl;
    }
};


int main()
{
                 
                 
    Test<int> t1(123);
    cout << t1.get_value() << endl; //123

    Projector p;
    Test<Projector> t2(p);
    t2.get_value().show(); // 投影仪播放内容中...

    return 0;
}

The functions in the above Test class can be declared and defined separately, written as follows:

#include 

using namespace std;

template <typename T> // typename可与class关键字替换
class Test
{
                 
                 
private:
    T value;

public:
    Test(T v);

    T get_value() const;
};

template <typename T>
Test<T>::Test(T v)
{
                 
                 
    value = v;
}

template <typename T>
Test<T>::get_value() const
{
                 
                 
    return value;
}

class Projector // 投影仪
{
                 
                 
public:
    void show()
    {
                 
                 
        cout << "投影仪播放内容中..." << endl;
    }
};


int main()
{
                 
                 
    Test<int> t1(123);
    cout << t1.get_value() << endl; //123

    Projector p;
    Test<Projector> t2(p);
    t2.get_value().show(); // 投影仪播放内容中...

    return 0;
}

2. Container

A container is a content belonging to the Standard Template Library (Standard Template Library, STL). STL is a concept proposed by Hewlett-Packard Labs and later introduced by C++.

Through algorithms, containers and iterators, combined with template technology, some types of generic programming are implemented to improve code reuse opportunities.

The container is used to store a collection of data elements, and the type of data elements can be any type. There are two types of containers: sequential containers and associative containers.

 

All containers can only use stack memory objects.

2.1 Sequential Containers

The storage elements of a sequential container are distributed in a linear relationship, and elements at fixed positions can be accessed through subscripts or iterators, and the position of elements can be changed by operations such as insertion or deletion.

2.1.1 array array (familiar)

Array was introduced in C++11, which is safer and easier to use than built-in arrays.

#include 
#include  // 容器类都需要引入头文件

using namespace std;

int main()
{
                 
                 
    // 创建一个长度为5的数组对象
    array<int,5> arr = {
                 
                 1,2,3};

    // 前三个元素的值是1,2,3
    cout << arr[0] << endl; // 1
    // 默认值0(不同的编译环境可能有所区别)
    cout << arr[3] << endl; // 0

    // 修改第四个元素的值
    arr[3] = 888;
    cout << arr[3] << endl; // 888
    // 也支持at函数(推荐)
    cout << arr.at(3) << endl; // 888

    cout << "------普通for循环-------" << endl;
    for(int i = 0;i<arr.size();i++)
    {
                 
                 
        cout << arr.at(i) << " ";
    }
    cout << endl;

    cout << "------for each循环-------" << endl;
    for(int i:arr)
    {
                 
                 
        cout << i << " ";
    }
    cout << endl;

    // 迭代器(略)

    return 0;
}

2.1.2 vector vector (master)

The vector is internally implemented by an array. Random access (reading and writing at fixed positions) is more efficient, and the efficiency of insertion and deletion operations is lower than that of insertion and deletion. It supports subscript operations.

#include 
#include  // 容器类都需要引入头文件

using namespace std;

int main()
{
                 
                 
    // 创建初始元素为5的向量对象
    vector<int> vec(5);
    cout << vec.size() << endl; // 5
    // 可以使用[]或at函数取出元素,推荐后者
    cout << vec.at(0) << endl; // 0
    // 判断是否为空
    cout << vec.empty() << endl; // 0
    // 尾部追加
    vec.push_back(8);
    cout << vec.at(vec.size()-1) << endl; // 8
    // 在第一个位置插入一个元素
    // 参数1:插入位置,begin函数返回一个迭代器指针,指向第一个元素
    // 参数2:插入内容
    vec.insert(vec.begin(),1);
    //  在倒数第二个位置插入一个元素
    // 参数1:end函数返回一个迭代器指针,指向最后一个元素的后面
    // 参数2:插入内容
    vec.insert(vec.end()-1,678);
    // 修改第二个元素
    vec[1] = 2;
    // 删除第二个元素
    vec.erase(vec.begin()+1);
    // 删除倒数第二个元素
    vec.erase(vec.end()-2);

    cout << "------普通for循环-------" << endl;
    for(int i = 0;i<vec.size();i++)
    {
                 
                 
        cout << vec.at(i) << " ";
    }
    cout << endl;

    vec.clear(); // 清空

    cout << "------for each循环-------" << endl;
    for(int i:vec)
    {
                 
                 
        cout << i << " ";
    }
    cout << endl;
    
    // 迭代器:略

    return 0;
}

2.1.3 list list (master)

The inside of the list is implemented by a doubly linked list, so the memory space of the elements is not continuous, and elements cannot be accessed through subscripts, but insertion and deletion operations can be performed efficiently.

#include 
#include  // 容器类都需要引入头文件

using namespace std;

int main()
{
                 
                 
    // 创建一个元素是4个hello的列表对象
    list<string> lis(4,"hello");
    // 判断是否为空
    cout << lis.empty() << endl; // 0
    // 向后追加元素
    lis.push_back("bye");
    // 头插
    lis.push_front("hi");
    // 在第二个位置插入元素“second”
    // 注意:迭代器指针不支持+运算,支持++运算
    lis.insert(++lis.begin(),"second");
    // 在倒数第二个位置插入元素"aaa"
    // 注意:迭代器指针不支持-运算,支持--运算
    lis.insert(--lis.end(),"aaa");

    // 到第5个位置插入元素“555”
    // 1. 先拿到第一个元素位置的迭代器指针
    list<string>::iterator iter =  lis.begin();
    // 2. 向后移动4位
    // 参数1:迭代器指针
    // 参数2:向后移动的数量,负数为向前
    advance(iter,4);
    // 3. 插入元素“555”
    lis.insert(iter,"555");

    // 修改第五个元素为“666”,【重新获取】并移动迭代器指针
    iter = lis.begin();
    advance(iter,4);
    *iter = "666";

    // 删除第一个元素
    lis.pop_front();
    // 删除最后一个元素
    lis.pop_back();
    // 删除第四个元素
    iter = lis.begin();
    advance(iter,3);
    lis.erase(iter);

    // 取出第一个和最后一个元素
    cout << lis.front() << " " << lis.back() << endl;

    // 不支持普通for循环遍历

    cout << "------for each循环-------" << endl;
    for(string i:lis)
    {
                 
                 
        cout << i << " ";
    }
    cout << endl;

    // 迭代器:略


    return 0;
}

2.1.4 deque queue (master)

The API is almost completely compatible with vector and list, and it is between vector and list in terms of performance.

The code is omitted.

2.2 Associative Containers (Mastery)

There is no strict order relationship among the elements, but there is a sorting feature inside to facilitate the iterator to traverse.

Elements are stored as key-value pairs, the key must be unique, and the value can be repeated; the key is usually a string type, and the value can be of any type.

#include 
#include  // 容器类都需要引入头文件

using namespace std;

int main()
{
                 
                 
    // 创建一个map对象,尖括号中分别为键与值的类型
    map<string,int> map1;

    // 插入数据
    map1["height"] = 177;
    map1["weight"] = 80;
    map1["age"] = 25;
    map1.insert(pair("salary",12000));

    // 取出元素
    cout << map1["salary"] << endl; // 12000
    // 更推荐使用at函数
    cout << map1.at("age") << endl; // 25

    // 修改元素
    map1["weight"] = 70;
    cout << map1["weight"] << endl;

    // 删除元素
    map1.erase("height");
    // 判断"身高"键值存不存在
    if(map1.find("age") != map1.end()) // 在
    {
                 
                 
        cout << "键值对存在!" << endl;
    }else // 不在
    {
                 
                 
        cout << "键值对不存在!" << endl;
    }

    cout << map1.size() << endl;
    // 清空
    map1.clear();
    cout << map1.size() << endl; // 0


    // 迭代器:略

    cout << "主函数直接完毕" << endl;
    return 0;
}

2.3 Iterators (mastering)

An iterator is a special pointer that uses a unified operation method for all container types to traverse.

If you want to use an iterator for read and write operations, the iterator type should be iterator; if you want to use an iterator for read-only operations, the iterator type is recommended to be const_iterator, which is slightly more efficient than iterator.

It is recommended to use iterators for traversal.

#include 
#include 
#include 
#include 
#include 
#include  // 容器类都需要引入头文件

using namespace std;

int main()
{
                 
                 
    string s = "djkshfjk";
    array<int,5> arr = {
                 
                 1,2,3,4,5};
    vector<int> vec(5,5);
    list<int> lis(5,5);
    deque<int> deq(5,5);
    map<string,string> m;
    m["fhdj"] = "fdfdfdf";
    m["fhj"] = "fdfdf";
    m["hdj"] = "fddf";

    // 迭代器遍历
    for(string::const_iterator iter = s.begin();iter!=s.end();iter++)
    {
                 
                 
        cout << *iter << " ";
    }
    cout << endl;

    for(array<int,5>::const_iterator iter = arr.begin();
        iter!=arr.end();iter++)
    {
                 
                 
        cout << *iter << " ";
    }
    cout << endl;

    for(vector<int>::const_iterator iter = vec.begin();
        iter!=vec.end();iter++)
    {
                 
                 
        cout << *iter << " ";
    }
    cout << endl;

    for(list<int>::const_iterator iter = lis.begin();iter!=lis.end();iter++)
    {
                 
                 
        cout << *iter << " ";
    }
    cout << endl;

    for(deque<int>::const_iterator iter = deq.begin();
        iter!=deq.end();iter++)
    {
                 
                 
        cout << *iter << " ";
    }
    cout << endl;

    for(map<string,string>::const_iterator iter = m.begin();
        iter!=m.end();iter++)
    {
                 
                 
        // 键使用first表示
        // 值使用second表示
        cout << iter->first << " " << iter->second << endl;
    }

    return 0;
}

Exercise: Templates

Write a function show_array that requires passing in two parameters void show_array(T t,int len)

The first parameter is the default value of the array, and the second parameter is the length of the array

The function of this function is to create an array whose length is len and whose type is T, and the default value is t, and output it. 


#include <iostream>
using namespace std;
template <class T>
void show_array(T t, int len){
T arr[len];
for(int i=0;i< len; i++){
arr[i] =t;
cout << arr[i]<<" ";
}
cout <<endl;
}
int main()
{
show_array(6,6);
show_array(6.66,6);
show_array("shizheyangzuoma",6);
return 0;
}

Six, object-oriented core

1. Inheritance (emphasis)

Inheritance is to create a new class based on an existing class, and the new class has the characteristics of the existing class. Existing classes are called "base classes" or "parent classes"; newly created classes are called "derived classes" or "subclasses".

Inheritance is one of the three major characteristics of object-oriented, reflecting the idea of ​​code reuse.

#include 

using namespace std;

class Father
{
                       
                       
private:
    string first_name = "张";
public:
    void job()
    {
                       
                       
        cout << "我是一名厨师" << endl;
    }

    string get_first_name() const
    {
                       
                       
        return first_name;
    }

    void set_first_name(const string& first_name)
    {
                       
                       
        this->first_name = first_name;
    }
};

/**
 * @brief The Son class 公有继承
 */
class Son:public Father
{
                       
                       
public:
    void play() // 新增内容
    {
                       
                       
//        cout << first_name << endl; 虽然继承了,无法直接访问
        cout << "我爱打游戏!" << endl;
    }

    // 函数隐藏
    void job()
    {
                       
                       
        cout << "我要做码农!" << endl;
    }
};


int main()
{
                       
                       
    Son s;
    cout << s.get_first_name() << endl; // 张
    s.job(); // 我要做码农!
    s.Father::job(); // 调用被隐藏的基类函数,输出:我是一名厨师
    s.play(); // 我爱打游戏!
    // 更改属性值
    s.set_first_name("王");
    cout << s.get_first_name() << endl; // 王

    return 0;
}

Derived classes can inherit the members of the base class, and can make necessary additions or modifications to it. A base class can have multiple derived classes, and derived classes can have multiple new derived classes, so the inheritance relationship is relative.

Usually base classes are more abstract and derived classes are more concrete.

The default inheritance is private permission, and public must be used currently.

2. Constructor in inheritance

2.1 Basic Features (Key Points)

Constructors and destructors cannot be inherited.

#include 

using namespace std;

class Father
{
                       
                       
private:
    string first_name;

public:
    Father(string first_name)
    {
                       
                       
        this->first_name = first_name;
    }

    string get_first_name() const
    {
                       
                       
        return first_name;
    }
};

class Son:public Father
{
                       
                       

};


int main()
{
                       
                       
//    Son s1("张"); 错误:构造函数不能被继承
//    Son s1; 错误:找不到Father::Father()构造函数

    return 0;
}

In inheritance, any constructor of any derived class must directly or indirectly call any constructor of the base class. Because when creating a derived class object, you need to call the code of the base class, and use the logic of the base class to open up a space for partial inheritance.

In the code in the previous section, without writing a constructor, the compiler will add no-argument constructors to the Father class and the Son class, and at the same time, try to call Father's no-argument constructor in the Son constructor.

In fact, as long as a derived class does not manually call the base class constructor, the compiler will try to call the base class's no-argument constructor.

The code above can have two solutions:

  • Add a no-argument call interface to the Father constructor
    • The Father class adds an overloaded no-argument constructor

    Father()
    {
                       
                       
        first_name = "张";
    }

    • Add a default value to Father's existing constructor to support no-argument calls.

    Father(string first_name = "张")
    {
                       
                       
        this->first_name = first_name;
    }

  • Add code to call the Father constructor to the Son class.

See Section 2.2.

2.2 The derived class constructor calls the base class constructor

There are three ways to call the constructor of the base class in the constructor of the derived class:

  • Transparent structure
  • delegate constructor
  • inheritance construct

2.2.1 Transparent transmission structure (emphasis)

Transparent construction refers to directly calling the constructor of the base class in the constructor of the derived class.

#include 

using namespace std;

class Father
{
                       
                       
private:
    string first_name;

public:
    Father(string first_name)
    {
                       
                       
        this->first_name = first_name;
    }

    string get_first_name() const
    {
                       
                       
        return first_name;
    }
};

class Son:public Father
{
                       
                       
public:
//    Son():Father(){} 如果不写构造函数,编译器自动添加
    Son():Father("王"){} // 透传构造
    Son(string name)
        :Father(name){} // 透传构造
};


int main()
{
                       
                       
    Son s1;
    cout << s1.get_first_name() << endl;
    Son s2("张");
    cout << s2.get_first_name() << endl;

    return 0;
}

2.2.2 Delegating Constructs (Mastery)

A constructor in the same class can call another overloaded constructor. It should be noted that in inheritance, the derived class constructor A delegates to other constructors, and eventually there must be a constructor that transparently calls the constructor of the base class .

#include 

using namespace std;

class Father
{
                       
                       
private:
    string first_name;

public:
    Father(string first_name)
    {
                       
                       
        this->first_name = first_name;
    }

    string get_first_name() const
    {
                       
                       
        return first_name;
    }
};

class Son:public Father
{
                       
                       
public:
    Son():Son("王"){} // 委托构造
    Son(string name)
        :Father(name){} // 透传构造
};

int main()
{
                       
                       
    Son s1;
    cout << s1.get_first_name() << endl;
    Son s2("张");
    cout << s2.get_first_name() << endl;

    return 0;
}

2.2.3 Inheritance construction (understanding)

Inheritance construction is a feature of C++11. It is not a real inheritance constructor, but the compiler automatically creates n constructors for derived classes (the number of n depends on the number of base class constructors), and each derived class The parameters of the constructor are the same as those of the base class, and the constructors of each derived class pass through the constructor with the same parameters of the base class.

#include 

using namespace std;

class Father
{
                       
                       
private:
    string first_name;

public:
    Father(string first_name)
    {
                       
                       
        this->first_name = first_name;
    }

    Father():Father("张"){}

    string get_first_name() const
    {
                       
                       
        return first_name;
    }
};

class Son:public Father
{
                       
                       
public:
    // 一句话搞定
    using Father::Father;

    // 相当于添加了下面的代码
//    Son(string first_name):
//        Father(first_name){}

//    Son():Father(){}
};


int main()
{
                       
                       
    Son s1;
    cout << s1.get_first_name() << endl;
    Son s2("张");
    cout << s2.get_first_name() << endl;

    return 0;
}

3. Object creation and destruction process (master)

#include 

using namespace std;

/**
 * @brief The Value class 作为其他类的变量
 */
class Value
{
                       
                       
private:
    string name;

public:
    Value(string name):name(name)
    {
                       
                       
        cout << name << "创建了" << endl;
    }

    ~Value()
    {
                       
                       
        cout << name << "销毁了" << endl;
    }
};

class Father
{
                       
                       
public:
    Value value = Value("Father类的成员变量");
    static Value s_value;

    Father()
    {
                       
                       
        cout << "Father类的构造函数" << endl;
    }

    ~Father()
    {
                       
                       
        cout << "Father类的析构函数" << endl;
    }
};

Value Father::s_value = Value("Father类的静态成员变量");

class Son:public Father
{
                       
                       
public:
    Value value2 = Value("Son类的成员变量");
    static Value s_value2;

    Son():Father()
    {
                       
                       
        cout << "Son类的构造函数" << endl;
    }

    ~Son()
    {
                       
                       
        cout << "Son类的析构函数" << endl;
    }
};

Value Son::s_value2 = Value("Son类的静态成员变量");


int main()
{
                       
                       
    cout << "程序开始执行" << endl;
    {
                       
                       
        Son s;
        cout << "对象s使用中......" << endl;
    }

    cout << "程序结束执行" << endl;
    return 0;
}

Observable rules:

1. The creation and destruction processes are completely symmetrical.

2. During the creation process, the functions of the same type are executed first by the base class, and then by the derived class.

3. The life cycle of static member variables is the same as the cycle of program operation.

Through the above examples, we can see the characteristics of object-oriented programming: high writing efficiency and low execution efficiency.

4. Multiple Inheritance (Mastery)

4.1 Basic use

C++ supports multiple inheritance, that is, a derived class can have multiple base classes.

#include 

using namespace std;

class Sofa
{
                       
                       
public:
    void sit()
    {
                       
                       
        cout << "能坐着!" << endl;
    }
};

class Bed
{
                       
                       
public:
    void lay()
    {
                       
                       
        cout << "能躺着!" << endl;
    }
};

class SofaBed:public Sofa,public Bed
{
                       
                       

};

int main()
{
                       
                       
    SofaBed sb;
    sb.sit();
    sb.lay();

    return 0;
}

4.2 Ambiguity problem

4.2.1 The first case

Ambiguity problems arise when multiple base classes have members with the same name.

#include 

using namespace std;

class Sofa
{
                       
                       
public:
    void position()
    {
                       
                       
        cout << "放在客厅" << endl;
    }

    void sit()
    {
                       
                       
        cout << "能坐着!" << endl;
    }
};

class Bed
{
                       
                       
public:
    void position()
    {
                       
                       
        cout << "放在卧室" << endl;
    }

    void lay()
    {
                       
                       
        cout << "能躺着!" << endl;
    }
};

class SofaBed:public Sofa,public Bed
{
                       
                       

};

int main()
{
                       
                       
    SofaBed sb;
    sb.sit();
    sb.lay();
//    sb.position(); 错误
    // 手动告诉编译器调用来自哪个基类的成员
    sb.Sofa::position();
    sb.Bed::position();

    return 0;
}

4.2.2 The second case

Diamond Inheritance: If multiple base classes of a derived class have a common base class, this is diamond inheritance.

The first solution is to directly write the members inherited from which base class.

#include 

using namespace std;

class Furniture // 家具
{
                       
                       
public:
    void show()
    {
                       
                       
        cout << "这是个家具" << endl;
    }
};

class Bed:public Furniture // 床
{
                       
                       

};

class Sofa:public Furniture // 沙发
{
                       
                       

};

class SofaBed:public Bed,public Sofa // 沙发床
{
                       
                       

};

int main()
{
                       
                       
    SofaBed sb;
//    sb.show(); 错误:二义性

    // 告诉编译器用哪个基类的代码
    sb.Bed::show();
    sb.Sofa::show();
//    sb.Furniture::show(); 错误:二义性


    return 0;
}

The second solution is to use virtual inheritance. In the virtual inheritance, a virtual base class pointer and a virtual base class table are added to Sofa and Bed classes. When Sofa and Bed are used as base classes, their derived class SofaBed class will also inherit Virtual base class pointer (does not inherit the virtual base class table). When the SofaBed class invokes the content of Furniture, it first uses the inherited virtual base class pointer to query in the virtual base class table to deal with the ambiguity problem.

#include 

using namespace std;

class Furniture // 家具
{
                       
                       
public:
    void show()
    {
                       
                       
        cout << "这是个家具" << endl;
    }
};

class Bed:virtual public Furniture // 床
{
                       
                       

};

class Sofa:virtual public Furniture // 沙发
{
                       
                       

};

class SofaBed:public Bed,public Sofa // 沙发床
{
                       
                       

};

int main()
{
                       
                       
    SofaBed sb;
    sb.show();

    return 0;
}

The disadvantage of virtual inheritance is that the calling speed is slower, because the process of looking up the table is increased.

5. Permissions

5.1 Three permission modifiers (emphasis)

Intra-class access

access in derived class

global access

Private permission private

X

X

Protection permissions protected

X

Public permission public

#include 

using namespace std;

class Father
{
                       
                       
private:
    string str = "Father的私有权限";

protected:
    string str2 = "Father的保护权限";

public:
    Father()
    {
                       
                       
        cout << str2 << endl;
    }
};

class Son:public Father
{
                       
                       
public:
    void test()
    {
                       
                       
//        cout << str << endl; 错误
        cout << str2 << endl;
    }
};

int main()
{
                       
                       
    Father f; // Father的保护权限
    Son s; // Father的保护权限
    s.test(); // Father的保护权限
//    cout << f.str2 << endl; 错误

    return 0;
}

5.2 Inheritance of different permissions

5.2.1 Public inheritance (emphasis)

Derived classes can inherit all privileged members of the base class, but cannot directly access the private members of the base class. For the protected members and public members of the base class, the derived class still has the original privileges.

#include 

using namespace std;

class Father
{
                       
                       
private:
    string str1 = "Father的私有权限";

protected:
    string str2 = "Father的保护权限";

public:
    string str3 = "Father的公有权限";
};

class Son:public Father
{
                       
                       


};

class Grandson:public Son
{
                       
                       
public:
    void test()
    {
                       
                       
        cout << str2 << endl;
        cout << str3 << endl;
    }
};

int main()
{
                       
                       
    Son s;
//    cout << s.str2 << endl; 错误
    cout << s.str3 << endl;

    Grandson gs;
    gs.test();

    return 0;
}

Public inheritance is the most commonly used inheritance method.

5.2.2 Protection Inheritance (Mastery)

The derived class can inherit all the members of the base class, but cannot directly access the private members of the base class. For the protected members and public members of the base class, they become protected rights in the derived class.

#include 

using namespace std;

class Father
{
                       
                       
private:
    string str1 = "Father的私有权限";

protected:
    string str2 = "Father的保护权限";

public:
    string str3 = "Father的公有权限";
};

class Son:protected Father
{
                       
                       


};

class Grandson:public Son
{
                       
                       
public:
    void test()
    {
                       
                       
        cout << str2 << endl;
        cout << str3 << endl;
    }
};

int main()
{
                       
                       
    Son s;
//    cout << s.str2 << endl; 错误
//    cout << s.str3 << endl; 错误

    Grandson gs;
    gs.test();

    return 0;
}

5.2.3 Private inheritance (mastery)

Derived classes can inherit all privileged members of the base class, but cannot directly access the private members of the base class. For the protected members and public members of the base class, they become private privileges in the derived class.

#include 

using namespace std;

class Father
{
                       
                       
private:
    string str1 = "Father的私有权限";

protected:
    string str2 = "Father的保护权限";

public:
    string str3 = "Father的公有权限";
};

class Son:private Father
{
                       
                       
public:
    void test()
    {
                       
                       
        cout << str2 << endl;
        cout << str3 << endl;
    }
};

class Grandson:public Son
{
                       
                       
public:
    void test()
    {
                       
                       
        //        cout << str2 << endl; 错误
        //        cout << str3 << endl; 错误
    }
};

int main()
{
                       
                       
    Son s;
    //    cout << s.str2 << endl; 错误
    //    cout << s.str3 << endl; 错误

    Grandson gs;
    gs.test();
    s.test();

    return 0;
}

6. Polymorphism (emphasis)

Polymorphism can be summarized as "one interface, multiple states". Only one function interface is written, and the code corresponding to the calling type is determined when the program is running.

The use of polymorphism requires the following conditions:

  • Must use public inheritance
  • Derived classes must have function coverage (overriding)
  • Base class reference/pointer to derived class object

6.1 Function coverage

Function coverage is very similar to function hiding, the difference is that the overridden function of the base class needs to be set as a virtual function.

The characteristics of virtual functions are as follows:

1. The virtual function is transitive. When the virtual function of the base class is overwritten, the overridden function of the derived class automatically becomes a virtual function.

2. Only non-static member functions and destructors can be defined as virtual functions.

3. If the declaration and definition are separated, you only need to use the virtual keyword to modify the function declaration.

#include 

using namespace std;

class Animal
{
                       
                       
public:
    virtual void eat() // 虚函数
    {
                       
                       
        cout << "动物吃东西" << endl;
    }
};

class Cat:public Animal
{
                       
                       
public:
    void eat() // 虚函数
    {
                       
                       
        cout << "猫吃鱼" << endl;
    }
};

class Dog:public Animal
{
                       
                       
public:
    void eat() // 虚函数
    {
                       
                       
        cout << "狗吃骨头" << endl;
    }
};

6.2 Using Polymorphism

Polymorphism is usually used with function parameters. Write two functions separately to trigger polymorphism of reference and pointer types.

#include 

using namespace std;

class Animal
{
                       
                       
public:
    virtual void eat() // 虚函数
    {
                       
                       
        cout << "动物吃东西" << endl;
    }
};

class Cat:public Animal
{
                       
                       
public:
    void eat() // 虚函数
    {
                       
                       
        cout << "猫吃鱼" << endl;
    }
};

class Dog:public Animal
{
                       
                       
public:
    void eat() // 虚函数
    {
                       
                       
        cout << "狗吃骨头" << endl;
    }
};

// 引用多态
void test_eat1(Animal& a)
{
                       
                       
    a.eat();
}

void test_eat2(Animal* a)
{
                       
                       
    a->eat();
}

int main()
{
                       
                       
    Animal a1;
    Cat c1;
    Dog d1;
    // 测试引用多态效果
    test_eat1(a1); // 动物吃东西
    test_eat1(c1); // 猫吃鱼
    test_eat1(d1); // 狗吃骨头

    Animal* a2 = new Animal;
    Cat* c2 = new Cat;
    Dog* d2 = new Dog;
    test_eat2(a2); // 动物吃东西
    test_eat2(c2); // 猫吃鱼
    test_eat2(d2); // 狗吃骨头

    return 0;
}

Exercise: Class Inheritance Use

Define the student class, which has private member variables such as name, student number, gender, age, etc., has constructors and destructors, and member functions for printing information.
It is required that the initial value of the attribute can be assigned through the constructor.

Define the class of college students, which inherits from the class of students. College students have private member variables of professional name and grades, and member functions of whether they have won scholarships (the grades are the basis for judging). The member function of base class printing information is hidden, and the new member function of printing information should also be able to print name, student number, gender, and age information.
It is required that the initial value of the attribute can be assigned through the constructor.

Then define the postgraduate class, inherited from the college class, with private member variables for tutor name and salary, and a member function for printing salary.
It is required that the initial value of the attribute can be assigned through the constructor.

#include <iostream>

using namespace std;
class Student
{
private:
    string name;
    int num;
    string gender;
    int age;

public:
    Student(string name,int num,string gender,int age):name(name),num(num),gender(gender),age(age){}
    void show()
    {
        cout<<name<<" "<<num<<" "<<gender<<" "<<age<<endl;
    }

    ~Student(){}

};

class Collagestudent:public Student
{
private:
    string majoy;
    double score;

public:
    Collagestudent(string name,int num,string gender,int age,string majoy,double score):Student(name,num,gender,age),majoy(majoy),score(score){}
    void scholarship()
    {
        if(score>90)
        {
            cout<<"acquire scholarship"<<endl;
        }
        else
        {
            cout<<"no scholarship"<<endl;
        }
    }

    void show()
    {
        Student::show();
        cout<<majoy<<" "<<score<<endl;

    }

};

class Graduatestudent: public Student
{
private:
    string teacher;
    double wage;

public:
    Graduatestudent(string name,int num,string gender,int age,string teacher,double wage):Student(name,num,gender,age),teacher(teacher),wage(wage){}

    void show()
    {

        cout<<wage<<endl;
    }

};

int main()
{
    Student s("zhangsan",1,"nan",20);
    s.show();
    Collagestudent cs("lisi",2,"nan",21,"pe",99);
    cs.show();
    cs.scholarship();
    Graduatestudent gs("zhangsan",1,"nan",20,"jiaoshou",12345);
    gs.show();

}

6.3 Principle of polymorphism

When there is a virtual function in a class, the compiler will classify a virtual function table for this class, and there will be a virtual function table pointer in the object pointing to this table.

When there is inheritance and function coverage, the derived class will modify the virtual function table in its own class, and the virtual function table pointer of its object will point to this table.

When the reference or pointer of the base class points to the derived class object, the program will look up the corresponding virtual function through the virtual function table pointer and call the content during the running process.

This process is called dynamic type binding. The use of polymorphism will generate some additional calling overhead, so polymorphism will reduce the execution efficiency of the program, but it can improve the programming efficiency.

#include 

using namespace std;

class Animal
{
                       
                       
public:
    virtual void eat() // 虚函数
    {
                       
                       
        cout << "动物吃东西" << endl;
    }
};

class Cat:public Animal
{
                       
                       
public:
    void eat() // 虚函数
    {
                       
                       
        cout << "猫吃鱼" << endl;
    }
};

class Dog:public Animal
{
                       
                       
public:
    void eat() // 虚函数
    {
                       
                       
        cout << "狗吃骨头" << endl;
    }
};

// 引用多态
void test_eat1(Animal& a)
{
                       
                       
    a.eat();
}

void test_eat2(Animal* a)
{
                       
                       
    a->eat();
}

int main()
{
                       
                       
    Animal a1;
    Cat c1;
    Dog d1;
    // 测试引用多态效果
    test_eat1(a1); // 动物吃东西
    test_eat1(c1); // 猫吃鱼
    test_eat1(d1); // 狗吃骨头

    Animal* a2 = new Animal;
    Cat* c2 = new Cat;
    Dog* d2 = new Dog;
    test_eat2(a2); // 动物吃东西
    test_eat2(c2); // 猫吃鱼
    test_eat2(d2); // 狗吃骨头
    // 先别管delete
    
    return 0;
}

6.4 Virtual destructors

If the base class reference or pointer points to the derived class object, when using delete to destroy the object, only the destructor of the base class will be called, and the destructor of the derived class will not be called. At this time, if there are memory resources requested by new in the derived class, it will cause a memory leak problem.

#include 

using namespace std;

class Animal
{
                          
                          
public:
    virtual ~Animal()
    {
                          
                          
        cout << "基类的析构函数" << endl;
    }
};
class Dog:public Animal
{
                          
                          
public:
    ~Dog()
    {
                          
                          
        cout << "派生类的析构函数" << endl;
    }
};

int main()
{
                          
                          
    Animal* a = new Dog;
    delete a; //派生类的析构函数 基类的析构函数
    return 0;
}

Therefore, when designing a class, if this class will become the base class of other classes, even if the destructor does not write anything, it is necessary to write an empty destructor and add the virtual keyword to modify it, unless it is certain that this class will not Inherited by any class.

7. Abstract class

7.1 The concept of an abstract class

If our base class only expresses some abstract concepts and is not associated with specific objects, it can provide a framework for derived classes, which is an abstract class.

A class is abstract if it has at least one pure virtual function.

If a class is abstract, it must have at least one pure virtual function.

A pure virtual function is a special kind of virtual function. A pure virtual function has only a declaration and no definition.

Destructors of abstract classes should be written as virtual destructors.

#include 

using namespace std;

class Shape
{
                          
                          
public:
    //纯虚函数
    virtual void perimeter() = 0;
    virtual void area() = 0;
    virtual ~Shape(){}
};

int main()
{
                          
                          
//    Shape s; //错误
    return 0;
}

7.2 Use of abstract classes

As an algorithm framework, the abstract class is mainly used to make its derived classes implement all pure virtual functions. At this point, the derived class will become an ordinary class and can be instantiated.

#include 

using namespace std;
//形状类
class Shape
{
                          
                          
public:
    //纯虚函数
    virtual void perimeter() = 0;
    virtual void area() = 0;
    virtual ~Shape()
    {
                          
                          

    }
};
//圆形类
class Circle:public Shape
{
                          
                          
public:
    void perimeter()
    {
                          
                          
        cout << "周长:2πR" << endl;
    }
    void area()
    {
                          
                          
        cout << "面积:πR^2" << endl;
    }
};

int main()
{
                          
                          
    Circle c;
    c.perimeter();
    c.area();
    return 0;
}

 Another situation is that the derived class of the abstract class only implements part of the pure virtual functions, which means that the derived class is still an abstract class, and its pure virtual functions must wait to be derived again until all pure virtual functions are implemented in the derived class.

#include 

using namespace std;

class Shape
{
                          
                          
public:
    //纯虚函数
    virtual void perimeter() = 0;
    virtual void area() = 0;
    virtual ~Shape()
    {
                          
                          

    }
};
//多边形类
class Polygon:public Shape
{
                          
                          
public:
    void perimeter()
    {
                          
                          
        cout << "周长:∑边长" << endl;
    }

};
//矩形类
class Rectangle:public Polygon
{
                          
                          
public:
    void area()
    {
                          
                          
        cout << "面积" << endl;
    }
};

int main()
{
                          
                          
    //    Polygon pg; 还是抽象类,只实现了部分纯虚函数
    Rectangle ra;
    ra.perimeter();
    ra.area();
    return 0;
}

Abstract classes support polymorphism .

7. Exception handling

1. Definition of exception

An exception is a problem that occurs during the running of the program, that is, there is no problem with the syntax during compilation.

#include 
#include 

using namespace std;


int main()
{
                            
                            
    array<int,100> arr;
    cout << arr.at(12) << endl; //1875651656
    cout << arr.at(101) << endl; //程序会终止运行,并抛出一个异常对象

    cout << "程序执行结束" << endl;
    return 0;
}

During the running of the program, once an exception occurs, there are two situations:

  • The user has the code to catch the exception, and after the catch is successful, the code of the corresponding remedial measures can be executed for our current exception to ensure that the program continues to execute normally
  • The program cannot find the code to catch the exception at the place where the exception object is thrown. At this time, it will call to find the code to catch the exception again, and search layer by layer. If there is no code to catch the exception at each layer of calls, the program will terminate.

#include 
#include 

using namespace std;

void test1()
{
                            
                            
    array<int,100> arr;
    cout << arr.at(12) << endl; //1875651656
    cout << arr.at(101) << endl; //程序会终止运行,并抛出一个异常对象
}
void test2()
{
                            
                            
    test1();
}

int main()
{
                            
                            

    test2();
    cout << "程序执行结束" << endl;
    return 0;
}

2. Handling exceptions

Programmers have two ways of handling exceptions in their code:

  • Throw an exception

Programmers can also manually use the throw keyword to throw exception objects to intervene in the process of program execution.

  • catch exception

Catching an exception is aimed at the thrown exception object, and after reasonable processing, the program continues to execute after being repaired.

2.1 Throwing exceptions

The exception object thrown by throw can be of any type. The following example uses const char* as the thrown exception object.

#include 
#include 

using namespace std;

double division(double a,double b)
{
                            
                            
    if(== 0)
    {
                            
                            
        throw "不能除以0";
    }
    return a/b;
}

int main()
{
                            
                            
    cout << division(1,0) << endl; //infinite
    //terminate called after throwing an instance of 'char const*'

    cout << "程序执行结束" << endl;
    return 0;
}

2.2 Catch exceptions

If a piece of code throws an exception, catch the exception using try and catch code blocks.

Place code that may cause exceptions in the try code block. If an exception is thrown in the code in the try block, then jump to the catch block for exception type matching; if the code in the try block does not throw an exception, then the program executes Ignore the catch block after the try block.

#include 
#include 

using namespace std;

double division(double a,double b)
{
                            
                            
    if(== 0)
    {
                            
                            
        throw "不能除以0";

    }
    return a/b;
}


int main()
{
                            
                            
    try{
                            
                            
        cout << division(1,3) << endl; //infinite
    }catch(const char* e)
    {
                            
                            
        cout << e << endl; //异常对象的信息
        cout << "1/100,下次不要怎么样" << endl; //弥补措施

    }

    //terminate called after throwing an instance of 'char const*'

    cout << "程序执行结束" << endl;
    return 0;
}

Eight, smart pointer

1. Definition of smart pointer

1.1 Why use smart pointers

Objects in heap memory in C++ are created after new, and if you forget to delete, memory leaks will occur.

Languages ​​such as Java, C#, and JS provide some garbage collection mechanisms to deal with unused objects. Therefore, the concept of smart pointers was also introduced in C++98, and it tends to be perfected in C++11.

1.2 Functions of smart pointers

Using smart pointers, programmers can automatically destroy heap memory objects without delete, achieving an effect similar to stack memory objects.

The smart pointer is mainly used to manage the heap memory object, which encapsulates the pointer of the heap memory object into a stack object; when the life cycle of the external stack memory object ends, the managed heap memory object will be released in the destructor, thus Prevent memory leaks.

1.3 Classification of smart pointers

First of all, we have four kinds of smart pointers in C++, all of which are under the standard name space. When using them, we need to introduce the header file *include. These four kinds of pointers are:

  • auto_ptr (automatic pointer, introduced in C++98, currently recommended not to use)
  • unique_ptr (unique pointer, introduced by C++11)
  • shared_ptr (shared pointer, introduced by C++11)
  • weak_ptr (virtual pointer, introduced in C++11)
  • 2. The use of smart pointers

2.1 auto_ptr

The basic usage code of auto_ptr is as follows. ·

#include 
#include 

using namespace std;

class Test
{
                               
                               
private:
    string name;
public:
    Test(string name):name(name)
    {
                               
                               
        cout << "构造函数" << endl;
    }
    void show()
    {
                               
                               
        cout << name << "调用成员" << endl;
    }
    ~Test()
    {
                               
                               
        cout << name << "析构函数" << endl;
    }
};

int main()
{
                               
                               
    {
                               
                               
        //新创建的这个对象A交给ap1管理
        auto_ptr<Test> ap1(new Test("A"));
        Test* t = ap1.get(); //获取资源对象
        t->show();
//        delete t;  被智能指针管理的资源对象不要手动销毁

        //如果要恢复手动管理方式,请先释放智能指针ap1对对象A的所有权
        ap1.release();
        delete t;
        //ap2管理对象B
        auto_ptr<Test> ap2(new Test("B"));
        //ap2重新管理对象C
        ap2.reset(new Test("C"));
        ap2.get()->show();
    }

    cout << "主函数结束" << endl;
    return 0;
}


The real misuse of auto_ptr lies in the copy semantics. When the copy constructor or assignment operator operation is executed, the management right of the heap memory object held by the original smart pointer object will also be transferred to the new smart pointer object.

#include 
#include 

using namespace std;

class Test
{
                               
                               
private:
    string name;
public:
    Test(string name):name(name)
    {
                               
                               
        cout << "构造函数" << endl;
    }
    void show()
    {
                               
                               
        cout << name << "调用成员" << endl;
    }
    ~Test()
    {
                               
                               
        cout << name << "析构函数" << endl;
    }
};

int main()
{
                               
                               
    {
                               
                               
        auto_ptr<Test> ap1(new Test("A"));
        auto_ptr<Test> ap2(new Test("B"));
        auto_ptr<Test> ap3(new Test("C"));
        auto_ptr<Test> ap4(ap1); //拷贝构造函数
        auto_ptr<Test> ap5 = ap2; //隐式调用拷贝构造函数
        auto_ptr<Test> ap6;
        ap6 = ap3; //赋值运算符
        cout << ap1.get() << endl; //0
        cout << ap2.get() << endl; //0
        cout << ap3.get() << endl; //0
        cout << ap4.get() << endl; //0xf015e8
        cout << ap5.get() << endl; //0xf024e8
        cout << ap6.get() << endl; //0xf02688
//        ap1.get()->show();
    }

    cout << "主函数结束" << endl;
    return 0;
}

2.2 unique_ptr

As an improvement to auto_ptr, unique_ptr has the sole control over the heap memory object it holds, that is, syntactically shields the copy semantics.

#include 
#include 

using namespace std;

class Test
{
                               
                               
private:
    string name;
public:
    Test(string name):name(name)
    {
                               
                               
        cout << "构造函数" << endl;
    }
    void show()
    {
                               
                               
        cout << name << "调用成员" << endl;
    }
    ~Test()
    {
                               
                               
        cout << name << "析构函数" << endl;
    }
};

int main()
{
                               
                               
    {
                               
                               
        unique_ptr<Test> up1(new Test("A"));
        unique_ptr<Test> up2(new Test("B"));
        unique_ptr<Test> up3(new Test("C"));
//        unique_ptr up4(up1); //拷贝构造函数
//        unique_ptr up5 = up2; //隐式调用拷贝构造函数
//        unique_ptr up6;
//        up6 = up3; //赋值运算符

        up1.get()->show();
    }

    cout << "主函数结束" << endl;
    return 0;
}


unique_ptr provides the move function to complete the transfer of control,

#include 
#include 

using namespace std;

class Test
{
                               
                               
private:
    string name;
public:
    Test(string name):name(name)
    {
                               
                               
        cout << "构造函数" << endl;
    }
    void show()
    {
                               
                               
        cout << name << "调用成员" << endl;
    }
    ~Test()
    {
                               
                               
        cout << name << "析构函数" << endl;
    }
};

int main()
{
                               
                               
    {
                               
                               
        unique_ptr<Test> up1(new Test("A"));
        unique_ptr<Test> up2(new Test("B"));
        unique_ptr<Test> up3(new Test("C"));
        unique_ptr<Test> up4(move(up1)); //拷贝构造函数
        unique_ptr<Test> up5 = move(up2); //隐式调用拷贝构造函数
        unique_ptr<Test> up6;
        up6 = move(up3); //赋值运算符

        cout << up1.get() << endl;
        cout << up2.get() << endl;
        cout << up3.get() << endl;
        cout << up4.get() << endl;
        cout << up5.get() << endl;
        cout << up6.get() << endl;
    }

    cout << "主函数结束" << endl;
    return 0;
}

2.3 shared_ptr

The resources held by shared_ptr can be directly shared among multiple shared_ptr.

2.3.1 Initialization method

shared_ptr comes out to support traditional initialization methods, and also supports initialization using the make_shared function.

#include 
#include 

using namespace std;

class Test
{
                               
                               
private:
    string name;
public:
    Test(string name):name(name)
    {
                               
                               
        cout << "构造函数" << endl;
    }
    void show()
    {
                               
                               
        cout << name << "调用成员" << endl;
    }
    ~Test()
    {
                               
                               
        cout << name << "析构函数" << endl;
    }
};

int main()
{
                               
                               
    {
                               
                               
        //传统初始化方式
        shared_ptr<Test> sp1(new Test("A"));
        //make_shared初始化
        shared_ptr<Test> sp2 = make_shared("B");
        sp1.get()->show();
        sp2.get()->show();
    }

    cout << "主函数结束" << endl;
    return 0;
}

Compared with new, make_shared has the following characteristics:

  • better performance
  • safer
  • May cause delayed release of memory

2.3.2 Introducing counts

Every time a shared_ptr manages a resource, the reference count of the resource will increase by 1. After the destruction of each shared_ptr object that manages the resource, the reference count of the resource will decrease by 1. When the count decreases to 0, the held resource will be released. resource object.

#include 
#include 

using namespace std;

class Test
{
                               
                               
private:
    string name;
public:
    Test(string name):name(name)
    {
                               
                               
        cout << "构造函数" << endl;
    }
    void show()
    {
                               
                               
        cout << name << "调用成员" << endl;
    }
    ~Test()
    {
                               
                               
        cout << name << "析构函数" << endl;
    }
};

int main()
{
                               
                               
    shared_ptr<Test> sp5;
    {
                               
                               
        cout << "{" << endl;

        shared_ptr<Test> sp1 = make_shared("A");
        cout << "引用计数:" << sp1.use_count() << endl; //1
        shared_ptr<Test> sp2(sp1); //显示调用构造函数
        cout << "引用计数:" << sp2.use_count() << endl; //2
        shared_ptr<Test> sp3 = sp2; //隐式调用构造函数
        cout << "引用计数:" << sp3.use_count() << endl; //3
        shared_ptr<Test> sp4;
        sp4 = sp3; //赋值运算符
        cout << "引用计数:" << sp4.use_count() << endl; //4
        sp1.reset(); //释放并销毁(引用计数-1)资源
        sp2.reset();
        sp3.reset();
        cout << "引用计数:" << sp4.use_count() << endl; //1
        sp5 = sp4;
        cout << "引用计数:" << sp5.use_count() << endl; //2
        sp4.reset(); //引用计数为0,释放资源
        cout << "引用计数:" << sp5.use_count() << endl; //1


        cout << "}" << endl;
    }
    cout << "引用计数:" << sp5.use_count() << endl;
    cout << "主函数结束" << endl;
    return 0;
}

2.4 weak_ptr

weak_ptr is a smart pointer that does not control the life cycle of the object, but only provides an access method to manage the object. The purpose of introducing it is to assist the shared pointer shared_ptr to work, and weak_ptr will not affect the reference count.

#include 
#include 

using namespace std;

class Test
{
                               
                               
private:
    string name;
public:
    Test(string name):name(name)
    {
                               
                               
        cout << "构造函数" << endl;
    }
    void show()
    {
                               
                               
        cout << name << "调用成员" << endl;
    }
    ~Test()
    {
                               
                               
        cout << name << "析构函数" << endl;
    }
};

int main()
{
                               
                               
    shared_ptr<Test> sp1 = make_shared("A");
    {
                               
                               
        //       weak_ptr wp1(new Test("A")); 不能独立使用
        //创建一个虚指针weak_ptr
        weak_ptr<Test> wp1(sp1);
        cout << wp1.use_count() << endl; //1

        weak_ptr<Test> wp2 = wp1; //隐式调用构造函数
        cout << wp2.use_count() << endl; //3
        weak_ptr<Test> wp3;
        wp3 = wp2; //赋值运算符
        //        wp3.get()->show();
        sp1.reset();

        weak_ptr<Test> wp4;
        wp4 = wp1;
        cout << wp4.use_count() << endl; //0
    }
    cout << "主函数结束" << endl;
    return 0;
}

9. Supplementary content

1. nullptr

Used instead of NULL in C++11.

#include 
#include 

using namespace std;

void test(int)
{
             
             
    cout << 1 << endl;
}
void test(char*)
{
             
             
    cout << 2 << endl;
}

int main()
{
             
             
    //C++的源码NULL就是个0
    test(NULL);//1
    test(nullptr); //2
    return 0;
}

2. Type deduction

#include 
#include 

using namespace std;


int main()
{
             
             
    int i = 1;
    auto a1 = 1; //auto被推导为整型
    auto a2 = 1.2; //auto被推导为double
    auto a3 = new auto(10); //auto被推导为int*
    cout << *a3 << endl; //10
    return 0;
}

Note that auto cannot be used as a parameter type, nor does it support arrays

3. Input and output streams

3.1 Formatted output

 3.1.1 Digital base output

#include 
#include 

using namespace std;


int main()
{
             
             
    //展示当前的进制:八进制开头0,十六进制开头0x,十进制不变
    cout << showbase;

    //八进制
    cout << oct;
    cout << 1234 << endl; // 2322
    cout << 8 << endl; // 10
    //十六进制
    cout << hex;
    cout << 255 << endl; //ff
    cout << 16 << endl; //10
    //切回十进制
    cout << dec;
    cout << 1234 << endl; //1234
    cout << 8 << endl; //8

    //不显示进制
    cout << noshowbase;
    return 0;
}

3.1.2 Domain Output (Output Domain)

Set the width of the output content through the setw function, there are two situations:

  • When the set width is smaller than the width of the data itself, it is displayed as the width of the data itself
  • When the set width is greater than the width of the data itself, it is displayed as the set width

#include 
#include 

using namespace std;


int main()
{
             
             
    cout << setw(10) << "124" << setw(10) << 244 << endl;
    cout << setw(10) << "1" << setw(10) << 244786756565656 << endl;
    return 0;
}

3.2 String stream

 Can complete the conversion between strings and numbers, need to introduce the header file #include < sstream >

#include 
#include 

using namespace std;


int main()
{
             
             
//    int→string
   int a = 123;
   stringstream ss;
   ss << a;
   string s = ss.str();
   cout << s.append("lala") << endl; //123lala
   //string→int
   istringstream is(s);
   int i;
   is >> i;
   cout << i+1 << endl; //124

    return 0;
}

Guess you like

Origin blog.csdn.net/qq_52049228/article/details/130924527