【C++】C++11

C++11

1. Uniform list initialization

1.1 {} initialization

C++11 expands the use of curly-brace-enclosed lists. Make it available for all built-in types and user-defined types. When using an initialization list, an equal sign (=) may or may not be added

2. Declaration

2.1 auto

In C++11, it is used to implement automatic type inference, which requires explicit initialization, so that the compiler can set the type of the defined object to the type of the initialization value.

2.2 decltype

declares the variable to be of the type specified by the expression

2.3 nullptr

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif

3. Rvalue references and move semantics

3.1 Lvalue references and rvalue references

There is a concept of reference in the traditional C++ grammar, and the rvalue reference syntax feature has been added in C++11. Whether it is an lvalue reference or an rvalue reference, it aliases the variable

What is an lvalue? What is an rvalue?

An lvalue represents an expression of data (such as a variable name or a dereferenced pointer), and we can get its address + assign a value to it. An lvalue can appear on the left side of an assignment symbol, an rvalue cannot appear on the left side of an assignment symbol

int main() {
    
    

    // 以下都是左值
    int* p = new int(0);
    int b = 1;
    const int c = 2;
    // 一下都是左值的引用
    int*& rp = p;
    int& rb = b;
    const int& rc = c;
    int& value = *p;
    return 0;
}

What is an rvalue? What is an rvalue reference?

An rvalue is also an expression of a data. Such as: literal constant, expression return value, function return value (this cannot be an lvalue reference return). An rvalue cannot appear on the left side of an assignment symbol, and an rvalue cannot take an address . An rvalue reference is a reference to an rvalue, aliasing the rvalue.


int main()
{
    
    
    double x = 1.1, y = 2.2;
// 以下几个都是常见的右值
    10;
    x + y;
    fmin(x, y);
// 以下几个都是对右值的右值引用
    int&& rr1 = 10;
    double&& rr2 = x + y;
    double&& rr3 = fmin(x, y);
// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
    10 = 1;
    x + y = 1;
    fmin(x, y) = 1;
    return 0;
}

It should be noted that the rvalue cannot take the address, but after aliasing the rvalue, it will cause the rvalue to be stored in a special location, and the address of that location can be taken.

int main() {
    
    
    double x = 1.1, y = 2.2;
    int&& rr1 = 10;
    const double&& rr2 = x + y;

    rr1 = 20;
    rr2 = 5.5; // 报错
    return 0;
}

3.2 Comparison of lvalue references and rvalue references

Summary of lvalue references:

  1. Lvalue references can only refer to lvalues, not rvalues
  2. A const lvalue reference can refer to either an lvalue or an rvalue

Summary of rvalue references:

  1. Rvalue references can only refer to rvalues, not lvalues
  2. Rvalue references can refer to lvalues ​​after move

3.3 Scenarios and meanings of rvalue references

Shortcomings of lvalue references:

image-20230709205301133

Only return by value can be used, and return by value will result in at least one copy construction

The essence of the move construction is to steal the resource of the rvalue of the parameter and take it as its own. Then there is no need to do deep copy, so it is called move construction. is to steal other people's resources to construct their own

Not just move construction, but move assignment

qsnpfu5bti-1688907511110.png

3.4 Perfect forwarding

The && in the template does not represent an rvalue reference. But universal references. Can accept both lvalues ​​and rvalues. The template's universal reference is only provided to receive lvalue references and rvalue references at the same time. But the only function of the reference type is to limit the received type, and subsequent references degenerate into lvalues. If you keep the lvalue or rvalue attributes during the transfer process, you need this perfect forwarding

forwardGuaranteed native type properties in the process of parameter passing

Keyword to force generation of default functionsdefault

For example, if we provide a copy structure, the move structure will not be generated. We can use defaultkeywords to display the generation of the specified move structure

Keyword that disallows generation of default constructorsdelete

4. Variadic templates

template<class...Args>
void show(Args...args) {
    
    
    
}

Expand the parameter pack recursively:

// 递归终止函数
template <class T>
void ShowList(const T& t)
{
    
    
    cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{
    
    
    cout << value <<" ";
    ShowList(args...);
}
int main()
{
    
    
    ShowList(1);
    ShowList(1, 'A');
    ShowList(1, 'A', std::string("sort"));
    return 0;
}

5. lambda expression

A lambda expression is actually an anonymous function

The writing format of lambda expression:

[capture-list] (parameters) mutable->return-type{statement}

int main() {
    
    
    int a = 3, b = 4;
    [=]{
    
    return a + b;};

    auto fun1 = [&](int c){
    
    
        b = a + c;
    };
    fun1(10);
    cout << b << " " << a << endl;
    // 复制捕捉x
    int x = 20;
    auto add_x = [x](int a) mutable {
    
    
        x *= 2;
        return a + x;
    };
    cout << add_x(10) << endl;
    cout << typeid(a).name() << endl;
    cout << typeid(add_x(10)).name() << endl;
}
  • [var] indicates the value transfer method, capturing the variable var
  • [=]: Indicates the value transfer method, capturing all variables in the parent scope
  • [&var]: Indicates that the capture variable var is passed by reference
  • [&] : Indicates that variables in all parent scopes are passed by reference
  • [this]: Indicates that the value transfer method captures the current this pointer

image-20230709213816350

5.1 Function objects and lambda expressions

A function object, also known as a functor, is an object that can be used like a function. It is a class object that overloads the operator() operator in the class

al3cwq0fkd-1688910086295.png

6. Thread library

6.1 A brief introduction to the thread class

Before C++11, issues involving multithreading were platform-related. Both windows and Linux have their own interfaces, which makes the code less portable. The most important feature of C++11 is the support for threads, so that C++ does not need to rely on third-party libraries when programming

Notice:

  1. Thread is a concept in the operating system. A thread object can be associated with a thread to control the thread and obtain the state of the thread

  2. When creating a thread, no thread function is provided, and the object does not actually correspond to any thread

  3. When a thread object is created and the thread is associated with a thread function, the thread is started. run alongside the main thread. Thread functions are generally provided in the following three ways:

    1. function pointer
    2. lambda expression
    3. function object

6.2 Thread function parameters

Thread function parameters are copied to the thread stack space in the form of value copy. Therefore, even if the thread parameter is a reference type, the external actual parameter cannot be modified after being modified in the thread. Because the actual reference is the copy in the thread stack, not the external actual parameter

Two threads alternately print odd and even numbers

#include "iostream"
#include "condition_variable"
#include "mutex"
#include "thread"
 using namespace std;
 //支持两个线程交替打印,t1打印奇数,t2一个打印偶数
int main(){
    
    
	mutex mtx;
	condition_variable cv;
	int n = 100;
	int x = 1;
	// 问题1:如何保证t1先运行,t2阻塞?
	// 问题2:如何防止一个线程不断运行?
	thread t1([&, n]() {
    
    
		while (true){
    
    
			unique_lock<mutex> lock(mtx);
			if (x >= 100)
				break;
			if (x % 2 == 0) // 偶数就阻塞
			{
    
    
				cv.wait(lock);
			}
//			cv.wait(lock, [&x]() {return x % 2 != 0; });
			cout << this_thread::get_id() << ":" << x << endl;
			++x;
			cv.notify_one();
		}
		});
	thread t2([&, n]() {
    
    
		while (true){
    
    
			unique_lock<mutex> lock(mtx);
			if (x > 100)
				break;
			if (x % 2 != 0) // 奇数就阻塞
			{
    
    
				cv.wait(lock);
			}
//			cv.wait(lock, [&x](){return x % 2 == 0; });
			cout << this_thread::get_id() << ":" << x << endl;
			++x;
			cv.notify_one();
		}
		});
	t1.join();
	t2.join();
	return 0;
}

7. Wrapper

function包装器Also called an adapter, let's see why a wrapper is needed? Essentially a class template and also a wrapper.

An example usage of the wrapper:

Before using the wrapper:

class Solution {
    
    
public:
    static int evalRPN(vector <string> &tokes)
    {
    
    
        stack<int> s;
        for (auto&str : tokes)
        {
    
    
            if (str == "+" || str == "-" || str == "*" || str == "/")
            {
    
    
                int right = s.top();
                s.pop();
                int left = s.top();
                s.pop();
                switch(str[0])
                {
    
    
                    case '+':
                        s.push(left + right);
                        break;
                    case '-':
                        s.push(left - right);
                        break;
                    case '*':
                        s.push(left * right);
                        break;
                    case '/':
                        s.push(left / right);
                        break;
                    default:
                        break;
                }
            }
            else
            {
    
    
                s.push(stoi(str));
            }
        }
    }
};

After using the wrapper:

class Solution {
    
    
public:
    static int evalRPN(vector <string> &tokes)
    {
    
    
        stack<int> st;
        map<string, function<int(int, int)>> opFuncMap ={
    
    
                {
    
    "+", [](int i, int j){
    
    return i + j;}},
                {
    
    "-", [](int i, int j){
    
    return i - j;}},
                {
    
    "*", [](int i, int j){
    
    return i * j;}},
                {
    
    "/", [](int i, int j){
    
    return i / j;}}
        };
        for (auto& str : tokes)
        {
    
    
            if (opFuncMap.find(str) != opFuncMap.end())
            {
    
    
                int right = st.top();
                st.pop();
                int left = st.top();
                st.pop();
                st.push(opFuncMap[str](left, right));
            }
        }
    }
};

8.bind

The bind function is defined in the header file and is a function template. It is like a function wrapper (adapter) that receives a callable object and generates a new callable object to "adapt" to the parameter list of the original object.

Generally speaking, we can return a function fn that originally received N function parameters by binding some parameters to receive M parameters (M can be greater than N)

The general form of calling bind is:auto newCallable = bind(callable, arg_list)

//表示绑定函数plus 参数分别由调用 func1 的第一,二个参数指定
std::function<int(int, int)> func1 = std::bind(Plus, placeholders::_1,placeholders::_2);

Guess you like

Origin blog.csdn.net/qq_63474430/article/details/131839639