进入新生活

看似简单,实则简单的玩意。。。

希望吧

统一的初始化方法

看下面的代码就知道了

int arr[3]{1,2,3};    //栈上
int *p = new int[20]{1,2,3};    //堆上,前三个元素初始化,其余的为随机

//stl
vector<int> v{1,2,3};
map<int, string> mp{{1,"a"}, {2,"B"}};
string str{"hello"};

//class
struct A{
    int i,j;
    A(int m,int n):i(m),j(n){};
};
A func(int m, int n){
    return {m,n};
}

int main(){
    A *pa = new A{3,7};
    mp.insert({3,"c"});
}

成员变量默认初始值

看代码理解吧

class B{
    public:
        int m = 1234;
        int n;
};
int main(){
    B b;
    cout << b.m << endl;
    return 0;
}

auto关键字

定义变量时,编译器可以自动判断变量的类型。

必须跟初始化语句才行。

auto i = 100;    //i是int
auto p = new A();    //p是A*
auto k = 3434LL;    //k是longlong

map<string, int, greater<string>> mp;
for(auto i = mp.begin();i!=mp.end();++i)
    pass;

class A{};

A operator+(int n, const A& a){
    return a;
}

template<class T1, class T2>
auto add(T1 x, T2 y)-> decltype(x+y) {
    return x+y;
}

auto d = add(100, 1.5);    //d是double
auto k = add(100, A());    //k是A类型,啥也没有
使用auto,必须搭配decltype(x+y)

必须使用后置返回值类型的方法重新声明

decltype关键字

求表达式的类型

int i;
double t;
struct A{double x;};
const A* a = new A();

decltype(a) x1;    //A*
decltype(i) x2; //int
decltype(a->x) x3;    //double
decltype((a->x)) x4 = t;    //double&

智能指针

  • 头文件:memory
  • 用途:

    让智能指针对象托管一个new运算符返回的指针

shared_ptr

  • 声明:

    shared_ptr<T> ptr(new T);

    从此ptr可以像T*类型的指针一样来使用,同时在ptr析构时,自动释放指针指向的内存空间。shared_ptr<>和原本指针无使用区别

  • 具体实现:

    shared_ptr内部,系统会自动维护一个指针的计数器,当某个指针的计数器为0时,系统自动delete该指针。

  • 使用:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    struct A {
        int n;
        A(int v = 0) : n(v) {}
        ~A() { cout << n << "destructor" << endl;}
    };
    
    int main(){
        shared_ptr<A> sp1(new A(2));
        shared_ptr<A> sp2(sp1);
        cout << "1. " << sp1->n << "," << sp2->n << endl;
        
        A *p = sp1.get();
        cout << "2. " << p->n << endl;
        cout << "2.5 count:" << sp1.use_count() << endl;
        
        shared_ptr<A> sp3;
        sp3 = sp1;
        cout << "3. " << sp3->n << endl;
        sp1.reset();
        if(!sp1)
            cout << "4. sp1 is NULL" << endl;
        
        A *q = new A(3);
        sp1.reset(q);
        cout << "5. " << sp1->n << endl;
        cout << "5.5 " << sp2->n << endl;
    
        shared_ptr<A> sp4(sp1);
        shared_ptr<A> sp5;
    
        sp1.reset();
        cout << "before and main" << endl;
        sp4.reset();
        cout << "end main" << endl;
    
    }
  • 函数

    1. 构造函数
    2. reset(),不填写为NULL,填写其他为指向某指针
    3. get()得到指针
    4. swap()交换指针
    5. use_count()使用次数
    6. unique()是否单一使用
  • 注意点:

    • shared_ptr对象不能直接托管指向动态分配数组的指针,需要在声明式加上[]即可

      不支持指针的加减操作!

      int main(){
       unique_ptr<int[]> up1(new int[10]);
       for(int i=0;i<10;++i)
           up1[i] = 10-i;
       sort(up1.get(), up1.get()+10);
       for_each(up1.get(), up1.get()+10, [](int& x){cout << x << endl;});
       return 0;
      }
    • 由于维护了计数器,性能稍弱
    • reset()函数不会增加ptr中对p的托管计数,看代码理解

      struct A {
          ~A() { cout << "destructor" << endl;}
      };
      
      int main(){
          A *p = new A();
          shared_ptr<A> ptr(p);
          shared_ptr<A> ptr2;
          ptr2.reset(p);
          cout << "end" << endl;
          return 0;
      }

      程序会报错,因为A被释放了两次

unique_ptr

shared_ptr类似,但是无法进行复制。

但是效率很高。

支持使用move()函数转移指针,看代码

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

struct A {
    int n;
    ~A() { cout << "destructor" << endl;}
};

int main(){
    unique_ptr<A> up1(new A{1});
    unique_ptr<A> up2 = move(up1);
    cout << up2->n << endl;
    return 0;
}

借助这种方法,我们就可以把它塞到必须执行拷贝命令的stl里面了

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

struct A {
    int n;
    ~A() { cout << "destructor" << endl;}
};

int main(){
    unique_ptr<A> up1(new A{1});
    vector<unique_ptr<A> > v;
    v.push_back(move(up1));
    cout << v[0]->n << endl;
    return 0;
}
  • 支持的函数

    • get()
    • release()
    • reset()
    • swap()

weak_ptr

看这篇文章吧:

作者:小石王

连接:https://www.cnblogs.com/xiaoshiwang/p/9721763.html

nullptr

NULL关键字同理,支持转换为bool类型的false

更加优美的for循环

看代码理解

struct A {
    int n;
    A(int i):n(i){}
};

int main(){
    int ary[] = {1,2,3,4,5};
    for(int &e : ary)   e *= 10;
    for(int e: ary) cout << e << ",";
    cout << endl;
    
    vector<A> st(ary,ary+5);
    for(auto& it : st)  it.n *= 10;
    for(A it:st)    cout << it.n << endl;
    
    return 0;
}

右值引用和move语义

一般来说,不能取地址的表达式,就是右值。

能取地址的,就是左值。默认的引用只能引用左值!

class A{};
A &r = A();    //error, A()返回一个临时变量,是右值
A &&r = A();    //ok,r是右值引用

目的是提高程序的效率,减少需要进行深拷贝的对象进行深拷贝的次数。

推荐上网自己查查

实际使用

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

class String{
    public:
        char* str;
        String(){
            str = new char[1];
            str[0] = 0;
        }
        String(const char* s){
            str = new char[strlen(s)+1];
            strcpy(str,s);
        }
        String(const String& s){
            cout << "copy constructor called" << endl;
            str = new char[strlen(s.str)+1];
            strcpy(str, s.str);
        }
        String& operator = (const String &s){
            cout << "copy operator = called" << endl;
            if(str!=s.str){
                delete[] str;
                str = new char[strlen(s.str)+1];
                strcpy(str,s.str);
            }
            return *this;
        }

        //移动构造函数
        String(String && s):str(s.str){
            cout << "move constructor called" << endl;
            s.str = new char[1];
            s.str[0] = 0;
        }
        //move assigment
        String& operator= (String && s){
            cout << "move operator= called" << endl;
            if(str!=s.str){
                delete []str;
                str = s.str;
                s.str = new char[1];
                s.str[0] = 0;
            }
            return *this;
        }
        ~String(){
            delete[] str;
        }
};

template<class T>
void MoveSwap(T& a, T& b){
    T tmp(move(a)); //调用move constructor
                    //move负责把左值转为右值
                    //把a的指针给tmp,创建一个临时的指针给a
    a = move(b);    //把b的指针给a,把临时指针给b
    b = move(tmp);  //把tmp(原a)给b,把临时指针给tmp

    //函数结束时,析构tmp,释放tmp
}

int main(){
    String s;
    s = String("ok");    //String("ok")是右值
    cout << "******" << endl;

    String &&r = String("this");
    cout << r.str << endl;
    
    String s1 = "hello", s2 = "world";
    MoveSwap(s1,s2);
    cout << s2.str << endl;
    return 0;
}

输出

move operator= called
******
this
move constructor called
move operator= called
move op
使用move的前提:

不介意ab的值修改,即类中已经实现move语句

无序容器(哈希表)

参考1:

作者:yijan

链接:https://www.luogu.com.cn/blog/yijan/unordered

参考2:

作者:ChasingdreamLY

链接:https://blog.csdn.net/qq_26591517/article/details/79621348

推荐额外阅读,gcc的pbds:

作者:Chanis

链接:https://www.luogu.com.cn/blog/Chanis/gnu-pbds

用法和功能与map一样,但是哈希表更符合程序员的需求

二分查找很老了,该退休了
  • 头文件:unordered_map
  • 性能优势:插入和查找都是常数复杂度
  • 缺陷:内存消耗高

用起来和map差不多,看代码理解

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

int main(){
    unordered_map<string ,int> turingWinner;
    turingWinner.insert({"Dijkstra", 1972});
    turingWinner.insert({"Scott", 1976});
    turingWinner.insert({"Wilkes", 1967});
    turingWinner.insert({"Hamming", 1968});
    turingWinner["Ritchie"] = 1983;

    string name;
    cin >> name;

    auto p = turingWinner.find(name);
    if(p!=turingWinner.end())
        cout << p->second << endl;
    else
        cout << "Not Found" << endl;
    return 0;
}

正则表达式

  • 头文件regex
  • 速度。。。我觉着不快

看代码理解

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

int main(){
    regex reg("b.?p.*k");
    // b开头
    // 。是任意字符
    // ?表示之前的出现1次或者0次
    // 这里表示b和p之间有1个或者0个字符
    // p是下一个字符
    // * 是任意次字符
    //表示p和k之间可以出现任意字符或0个字符
    cout << regex_match("bopggk", reg) << endl; //输出1
    cout << regex_match("boopgggk", reg) << endl;   //输出0,两个o
    cout << regex_match("b pk", reg) << endl;   //输入1
    

    regex reg2("\\d{3}([a-z A-Z]+).(\\d{2}|N/A)\\s\\1");
    // “\\d”表示\d,为数字
    // {3}表示出现三次
    // \\d{3} 表示开头是三个数字
    // ()表示为一个项,括号本身不参与匹配(加转义斜杠开始匹配)
    // [a-z A-Z]表示字母出现一次或若干次
    // + 表示前面的东西出现一次或任意多次
    // [a-z A-Z] 表示英文字母至少出现一次
    // 。 表示任意一个字符
    // 第二项中的 "|"" 表示或
    // "N/A" 就是简单的字母
    // \\s 表示一个空格
    // \\1 表示与第一项的内容是否相同(第一个圆括号中的内容,在这个式子里就是 ([a-z A-Z]+) )

    string correct = "123Hello N/A Hello";
    string incorrect = "123Hello 12 hello";
    cout << regex_match(correct, reg2) << endl;
    cout << regex_match(incorrect, reg2) << endl;
    
}

输出

1
0
1
1
0

Lambda

直接阅读https://hlz.ink/archives/67.html

Last modification:April 28th, 2020 at 05:08 pm
Compared with money, your comment could inspire me more.
相较于钱财,你的留言更能激发我创作的灵感