C++进阶7:c++11那些事

文章目录

    • 1\. auto
    • 2\. nullptr
    • 3\. lambda表达式
    • 4\. 关键字(default,delete,final)
    • 5\. 左值引用与右值引用
      • 5.1小测验
    • 6\. 完美转发

1. auto

1.typeid(*).name() -----(类型的查看)

l类型 显示
int i
float f
char* PKc (pointer–const–char)
int arr[] typeid(arr).name()
typeid(string).name() 好长的字符串(string);
typeid(vector).name() 好长的字符串(vector)
typeid(Simple).name() 类的话(*个字符)
auto it = vec.begin();cout << typeid(it).name() << endl; 好长的字符串(iterator)
func func

注意:
(1)auto 进行变量定义必须初始化;

auto m1= 10//必须要初始化;

(2)auto不能推导数组;

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

2.基于范围的for循环

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

3.定义迭代器

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

(4) 定义函数指针

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

完整代码见001——auto.cpp

#include 
#include 
#include 
using namespace std;

class Simple /*final*/{
      // 禁止继承

};

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


int main(){
     
    // auto 自动推导变量类型
    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; //好长的字符串(string);
    cout << typeid(vector<int>).name() << endl;//好长的字符串(vector);
    cout << typeid(Simple).name() << endl; //类的话(6个字符)
    
    auto m1= 10;//必须要初始化;
    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)注意:不能推导出数组





 // (2)基于范围的for循环
    for(auto a:arr){
     
        cout << a << " ";
    }
    cout << endl;

    // (3)定义迭代器
    vector<int> vec = {
     1,2,3};
    //vector::iterator it = vec.begin();
    auto it = vec.begin();
    cout << typeid(it).name() << endl;好长的字符串(iterator);

    // (4)定义函数指针
    auto func = Func;
    func();
    cout << typeid(func).name() << endl; //FUNC


    

}

2. nullptr

(1) nullptr表示c++里面的空指针
(2)C中NULL和C++中的NULL是由区别的
(c里面:#define NULL ((void*)0)
c++:#define NULL 0
所以c++里面再分配内存的时候,代表的是0,需要强制转化为指针;
(3) NULL定义在stddef.h

完整代码见002_nullptr.cpp

#include 
#include 
#include 
using namespace std;



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

int main(){
     
   

    // nullptr表示空指针
    // C中NULL和C++中的NULL是由区别的(c里面:#define NULL ((void*)0)   c++:#define NULL 0  )所以c++里面再分配内存的时候,代表的是0,需要强制转化为指针;
    // NULL定义在stddef.h

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


}

3. lambda表达式

lambda表达式
捕获列表->返回值类型{执行语句;}捕获的方式比较任意,最错误的写法:全用=
(1)值形式捕获,外边的数值不发生变化;默认用=代替;必须指定特殊的;引用进行捕获并修改;默认用&;必须指定特殊的;

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

完整代码见003_lamada.cpp

#include 
#include 
#include 
using namespace std;


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

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

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

    // lambda表达式
    // [捕获列表](参数列表)->返回值类型{执行语句;}捕获的方式比较任意,最错误的写法:全用=
    // (1)值形式捕获,外边的数值不发生变化;默认用=代替;必须指定特殊的;

    for_each(vec.begin(),vec.end(),[](int n){
     cout <<n<<",";});
    cout << endl;
    // 引用形式捕获
    int sum = 0;
    for_each(vec.begin(),vec.end(),[&sum](int n){
     sum += n;});  //(2)引用进行捕获并修改;默认用&;必须指定特殊的;
    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. 关键字(default,delete,final)

关键字 功能 example
default 使用默认函数 Simple() = default;
delete 禁止 Simple(const Simple&) = delete;
final (1)加上final,禁止继承;注释掉,可以继承 class Simple /*final*/
final (2)禁止重写 virtual void Funcl() const /*final*/
override 表示当前函数是重写父类的虚函数,避免重写的函数名出错的问题; virtual void Funcl()const override

完整代码见004_hot_words.cpp

#include 
#include 
#include 
using namespace std;

class Simple /*final*/{
      // (3.1)加上final,禁止继承;注释掉,可以继承
public:
    Simple() = default; // (1)使用默认构造函数
    Simple(const Simple&) = delete;//(2)禁止拷贝
    virtual void Funcl() const /*final*/{
      // (3.2)禁止重写/覆盖
    }
};

class SubSimple:public Simple{
     
    virtual void Funcl()const override{
      // (4)override表示当前函数是重写父类的虚函数,避免重写的函数名出错的问题;
    }
};




int main(){
     
   return 0;

    
}

5. 左值引用与右值引用

左值右值一直存在,只从右值引用出现,这对CP变得重要起来,那么为什么要有右值引用?没有右值引用C++不是活得好好的吗?一切要从左值引用说起。
在C++11之前,没有左值引用与右值引用之分,引用专指左值引用。那个时候就出现了不和谐的情况。

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

函数Print()能够打印s,却不能打印"abc"
自定义对象也会出现这种情况

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

在上面代码中,Func(d);编译执行都OK,而Func(Demo());编译出错。
如果说上一个是因为类型不匹配(一个是string,一个是char []),这一次都是Demo对象。同样都是对象怎么待遇不一样?问题就在于d是左值(有名对象),Demo()是右值(匿名对象/临时对象)。

那么,之前为什么很多应用过程中没有大规模的出现这个问题?
因为C++对const&添加特殊技能:既能接受左值又能接受右值。上面两个例子把函数参数加上const,即void Print(const string& s)void Func(const Demo &),问题解决了。
void Func(const Demo &)类型匹配解决了,为什么void Print(const string& s)也可以了呢?因为编译器自动把根据函数参数类型string,自动为"abc"构造一个临时对象string("abc"),这是默认类型转换。

但是这种解决方式只是解决了参数不变的情况,如果参数可变问题依然存在。例如:
有一个用户类,记录用户名、密码和登录次数。

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

提供一个函数bool Login(User& user);登录成功后,返回true,并且修改登录次数count

bool Login(User& user) {
     
    // 此处略去验证
    user.count++;
    return true
}

上面情况只能使用变量,不能使用右值(匿名对象),否则会出现上面的错误。但这种情况是存在的,只关注用户能否登录,不关心登录次数,使用右值(匿名对象),无可厚非,只是C++11之前语法不支持(实际中遇到这种请款,可以改设计改接口。)。于是,右值引用可以解决这种问题。重载上面的函数。

bool Login(User&& user) {
     
    // 此处略去验证
    user.count++;
    return true
}

这样Login()既可以接受左值又可以接受右值。

5.1小测验

  1. 回答下面程序输出结果并分析原因(如有编译错误请指出)
 #include 
    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. 回答下面程序输出结果并分析原因(如有编译错误请指出)
 #include 
    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. 回答下面程序输出结果并分析原因(如有编译错误请指出)
   #include 
    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. 完美转发

上面虽然把问题解决了,但是你会发现为了解决右值问题,每个函数都要写两边,并且函数名和函数体都一样,只是参数类型有区分,一个是左值,一个是右值。问题变得更加麻烦了。于是,在模板语法上添加了一个完美转发的语法。(C++滚雪球式添加新的语法,导致C++愈来愈复杂。)

上面两个Login()函数可以合并成

template<class T>
bool Login(T&& user) {
     
    // 此处略去验证
    user.count++;
    return true
}

注意,这里的&&已经不是右值引用了,而是被称为万能引用(universal references),而这种用法称为完美转发。
当传入右值时,模板特化成bool Login(User&& user),当传入左值时,模板特化成bool Login(User& user)。瞬间解决上面的问题。

&&在模板参数中表示万能引用,在非模板参数中表示右值引用。

你可能感兴趣的:(3.2,c++大神进阶(进阶篇))