C + + advanced 7: c++11

Article catalog

1. auto

1.typeid(*).name() ------ (view of type)

l type display
int i
float f
char* PKc (pointer–const–char)
int arr[] typeid(arr).name()
typeid(string).name() A long string;
typeid(vector).name() A long string (vector)
typeid(Simple).name() Class (* characters)
auto it = vec.begin();cout << typeid(it).name() << endl; Long string (iterator)
func func

be careful:
(1) auto must be initialized to define variables;

auto m1= 10//Must be initialized;

(2) auto can't deduce array;

auto m4[] = {1,2,3,4,5,6};
 cout << typeid(m4).name() << endl;

2. for loop based on scope

for(auto a:arr){
        cout << a << " ";
    }
    cout << endl;

3. Define iterator

  vector<int> vec = {1,2,3};
    //vector<int>::iterator it =  vec.begin (); / / not recommended
    auto it = vec.begin(); //recommend
    cout << typeid(it).name() << endl;

(4) Define function pointer

auto func = Func;
func();
cout << typeid(func).name() << endl;

See 001 for complete code—— auto.cpp

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

class Simple /*final*/{ // Prohibition of inheritance

};

void Func(){
    cout<< __func__<<endl;
}


int main(){
    // Auto auto deducing variable type
    int n = 10;
    cout << typeid(n).name() << endl;//i

    float f;
    cout << typeid(f).name() << endl;  //f

    const char* hello = "abc";
    cout << typeid(hello).name() << endl;  //PKc (pointer--const--char)

    int arr[] = {1,2,3,4,5};
    cout << typeid(arr).name() << endl;//Ai5(array_int_5)

    cout << typeid(string).name() << endl; //A long string;
    cout << typeid(vector<int>).name() << endl;//A long string (vector);
    cout << typeid(Simple).name() << endl; //Class (6 characters)
    
    auto m1= 10;//Must be initialized;
    auto m2= 10.0f;
    cout << typeid(m1).name() << endl; //i;
    cout << typeid(m2).name() << endl; //f;
    auto m3 = "abc";
    cout << typeid(m3).name() << endl;  //PKc (pointer--const--char)
   
    //auto m4[] = {1,2,3,4,5,6};
    //cout << typeid(m4).name() << endl;
   //(1) Note: arrays cannot be derived





 // (2) Range based for loop
    for(auto a:arr){
        cout << a << " ";
    }
    cout << endl;

    // (3) Define iterator
    vector<int> vec = {1,2,3};
    //vector<int>::iterator it = vec.begin();
    auto it = vec.begin();
    cout << typeid(it).name() << endl;////Long string (iterator);

    // (4) Define function pointer
    auto func = Func;
    func();
    cout << typeid(func).name() << endl; //FUNC


    

}

2. nullptr

(1) nullptr represents null pointer in c + +
(2) NULL in C and NULL in C + + are distinguished by
(in c: ාdefine NULL ((void*)0)
c++: #define NULL 0 )
Therefore, when reallocating memory in c + +, it represents 0, which needs to be forcibly converted into a pointer;
(3) NULL is defined in stddef.h

See 002 for complete code_ nullptr.cpp

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;



void Test(int* p){
    cout << "int*" << endl;
}
void Test(int n){
    cout << "int" << endl;
}

int main(){
   

    // nullptr for null pointer
    // The difference between NULL in C and NULL in C + + is (in C: ා define NULL ((void *) 0) and in C + +: ා define NULL 0). Therefore, when reallocating memory in C + +, it represents 0, which needs to be forcibly converted into a pointer;
    // NULL is defined in stddef.h

   // Test(NULL);
    Test(nullptr);
    Test(0);


}

3. lambda expression

lambda expressions
Capture list ->The return value type {execute statement;} is captured in an arbitrary and wrong way: all=
(1) Capture in the form of value, the value outside does not change; default = instead; special must be specified; reference to capture and modify; default &; special must be specified;

for_each(vec.begin(),vec.end(),[&res,num](int& n){res.push_back(n*n+num);});

See 003 for complete code_ lamada.cpp

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;


void Func(){
    cout << __func__ << endl;
}

void Test(int* p){
    cout << "int*" << endl;
}
void Test(int n){
    cout << "int" << endl;
}

int main(){
    // Define iterator
    vector<int> vec = {1,2,3};
    //vector<int>::iterator it = vec.begin();
    auto it = vec.begin();
    cout << typeid(it).name() << endl;

    // lambda expressions 
    // [capture list] (parameter list) - > return value type {execute statement;} the capture method is arbitrary and the most wrong way is to use all=
    // (1) Value form capture, the value of the outer side does not change; default = instead; special must be specified;

    for_each(vec.begin(),vec.end(),[](int n){cout <<n<<",";});
    cout << endl;
    // Reference form capture
    int sum = 0;
    for_each(vec.begin(),vec.end(),[&sum](int n){sum += n;});  //(2) Reference to capture and modify; default with &; must specify special;
    cout << sum << endl;


    int num = 100;    
    vector<int> res;
    for_each(vec.begin(),vec.end(),[&res,num](int& n){res.push_back(n*n+num);});
    for_each(res.begin(),res.end(),[](int n){cout <<n<<",";});
    cout << endl;

    
}

4. Keyword (default,delete,final)

keyword function example
default Use default function Simple() = default;
delete prohibit Simple(const Simple&) = delete;
final (1) Add final, prohibit inheritance; comment out, can inherit class Simple /*final*/
final (2) Disable override virtual void Funcl() const /*final*/
override Indicates that the current function is a virtual function rewriting the parent class to avoid the error of rewriting the function name; virtual void Funcl()const override

See 004 for complete code_ hot_ words.cpp

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

class Simple /*final*/{ // (3.1) add final to prohibit inheritance; if it is commented out, it can be inherited
public:
    Simple() = default; // (1) Use default constructor
    Simple(const Simple&) = delete;//(2) Copy forbidden
    virtual void Funcl() const /*final*/{ // (3.2) no rewriting / overwriting
    }
};

class SubSimple:public Simple{
    virtual void Funcl()const override{ // (4) override indicates that the current function is a virtual function rewriting the parent class to avoid the error of rewriting the function name;
    }
};




int main(){
   return 0;

    
}

5. Left value reference and right value reference

The left value and the right value always exist, only from the right value reference, which becomes important for CP, so why do we have the right value reference? Can't C + + live well without a right-hand reference? Everything starts with the left quotation.
Before C++11, there was no difference between left value reference and right value reference. Reference specifically refers to left value reference. At that time there was disharmony.

  #include <iostream>
    using namespace std;
    
    void Print(string& s){
        cout << s;
    }
    
    int main(){
        string s="abc";
        Print(s); // OK
        Print("abc"); // parse error
    }
    

Function Print() can print s, but not "abc".
This can also happen with custom objects

 #include <iostream>
    using namespace std;
    
    class Demo{};
    void Func(Demo &){
      cout << "Func(Demo &)" << endl;
    }
    
    int main(){
        Demo d;
        Func(d); // OK
        Func(Demo()); // parse error
    }

In the above code, Func(d); compilation execution is OK, while Func(Demo()); compilation error.
If the last one is due to type mismatch (one is string and the other is char []), this time it is Demo object. How are the same objects treated differently? The problem is that d is the left value (named object), and Demo() is the right value (anonymous object / temporary object).

So, why hasn't this problem occurred on a large scale in many applications before?
Because C + + adds special skills to const: it can accept both left and right values. The above two examples add const to function parameters, that is, void print (const string & S) and void func (const demo &), and the problem is solved.
Void func (const demo &) type matching has been solved. Why can void print (const string & S) also be used? Because the compiler automatically constructs a temporary object string("abc") for "abc" according to the function parameter type string, which is the default type conversion.

However, this solution only solves the problem of constant parameters. If the parameters are variable, the problem still exists. For example:
There is a user class that records the user name, password, and number of logins.

   struct User{
      string name;
      string pass;
      int count;
    }
    

Provide a function bool login (user & user); after the login is successful, return true, and modify the login count.

bool Login(User& user) {
    // Verification is omitted here
    user.count++;
    return true
}

In the above case, only variables can be used, and the right value (anonymous object) cannot be used. Otherwise, the above error will occur. But this kind of situation exists. It only focuses on whether the user can log in, does not care about the number of times of login, and uses the right value (anonymous object). There is no fault, but the syntax before C++11 does not support it (in reality, if this kind of request is encountered, the interface can be changed. ). Therefore, the right value reference can solve this problem. Overload the function above.

bool Login(User&& user) {
    // Verification is omitted here
    user.count++;
    return true
}

In this way, Login() can accept both left and right values.

5.1 quiz

  1. Answer the output of the following program and analyze the reason (if there is a compilation error, please point out)
 #include <iostream>
    using namespace std;
    
    class Demo{};
    void Func(const Demo &){
      cout << "Func(const Demo &)" << endl;
    }
    
    int main(){
        Demo d;
        Func(d);
        Func(Demo());
        const Demo cd;
        Func(cd);
    }
    
  1. Answer the output of the following program and analyze the reason (if there is a compilation error, please point out)
 #include <iostream>
    using namespace std;
    
    class Demo{};
    void Func(const Demo &){
      cout << "Func(const Demo &)" << endl;
    }
    
    void Func(Demo &){
      cout << "Func(Demo &)" << endl;
    }
    
    int main(){
        Demo d;
        Func(d);
        Func(Demo());
        const Demo cd;
        Func(cd);
    }
  1. Answer the output of the following program and analyze the reason (if there is a compilation error, please point out)
   #include <iostream>
    using namespace std;
    
    class Demo{};
    void Func(const Demo &){
      cout << "Func(const Demo &)" << endl;
    }
    
    void Func(Demo &){
      cout << "Func(Demo &)" << endl;
    }
    
    void Func(Demo &&){
      cout << "Func(Demo &&)" << endl;
    }
    
    int main(){
        Demo d;
        Func(d);
        Func(Demo());
        const Demo cd;
        Func(cd);
    }

6. Perfect forwarding

Although the above problem has been solved, you will find that in order to solve the right value problem, each function has to write two sides, and the function name and function body are the same, but the parameter types are different, one is left value and the other is right value. The problem has become more troublesome. Thus, a perfect forwarding syntax is added to the template syntax. (c + + snowballing adds new syntax, which makes C + + more and more complex. )

The above two Login() functions can be combined into

template<class T>
bool Login(T&& user) {
    // Verification is omitted here
    user.count++;
    return true
}

Note that the & & here is no longer a right value reference, but is called universal references, which is called perfect forwarding.
When the right value is passed in, the template is specialized into bool login (user & & user). When the left value is passed in, the template is specialized into bool login (user & user). Solve the above problems in an instant.

&&It represents universal reference in template parameter and right value reference in non template parameter.

Tags: Lambda

Posted on Sun, 14 Jun 2020 22:44:58 -0400 by johanlundin88