【C++】C++11部分特性

news2025/1/9 21:21:09

目录

一、初始化列表

二、变量类型的推导

1、auto

2、decltype

三、右值引用

1、左值与右值

2、关于左值引用、右值引用的问题

1、左值引用可以引用右值吗?

2、右值引用可以引用左值吗?

3、右值引用之后的问题

3、移动构造、移动拷贝

1、引用的价值

2、移动构造、移动赋值

4、完美转发

5、补充说明

四、关于默认成员函数的关键字

1、final

2、override

3、default

4、delete

四、可变参数模板

1、概念

2、模板包的展开

3、STL中具体应用

五、lambda表达式

1. lambda表达式各部分说明

2. 捕获列表说明

3、lambda表达式底层实现

六、包装器

1、包装器

2、bind

3、具体应用

总结


一、初始化列表

在C++98标准中{  },仅仅应用于数组,struct, class等

C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有的内置类型和用户自
定义的类型,使用初始化列表时,可添加等号(=),也可不添加

int a = 10;
int b{10};
int c = {10};

这三种写法都是可以的,并且STL的所有容器都支持初始化列表的方式进行初始化

   vector<int> v = {1, 2, 3, 4, 5, 55, 6, 7, 6, 4};

这样使用容器就会变得更加灵活,而所谓的初始化列表本质其实是在调用构造函数

以Date类举例


class Date
{
public:
    Date(int year, int month, int day)
        :_year(year)
        ,_month(month)
        ,_day(day)
    {
        cout << "Date(int year, int month, int day)" << endl;//为了证明初始化列表调用构造函数
    }
private:
    int _year;
    int _month;
    int _day;
};

int main()
{
    Date d1(2022, 11, 20);
    Date d2 = {2022, 11, 20};


    return 0;
}

 

其实所谓的初始化列表是C++11添加的一种容器

我们使用typeid查看它的类型

    auto it = {1, 2, 3, 4, 5, 6, 0, 7, 8, 9};
    cout << typeid(it).name() << endl;

 

 

同时STL的每一个容器的构造函数都多了一项 initializer_list

 

原本只有单参数构造函数支持的隐式类型转换,有了初始化列表,多参数构造函数也支持了

总结:C++11以后,一切对象都可以使用初始化列表,但是建议普通对象还是使用以前的方式初始化,容器有需要可以使用初始化列表

二、变量类型的推导

1、auto

在C++98中auto是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局
部的变量默认就是自动存储类型,所以auto就没什么价值了。C++11中废弃auto原来的用法,将
其用于实现自动类型推断。这样要求必须进行显示初始化,让编译器将定义对象的类型设置为初
始化值的类型。

//常用场景

unordered_map<string, int> m;

unordered_map<string, int>::iterator it = m.begin();

auto it = m.begin();

我们正常要写迭代器,变量的类型太长,这时可以使用auto,简化代码,让编译器自动推断类型

2、decltype

关键字decltype将变量的类型声明为表达式指定的类型

乍一看decltype与auto十分的相似,但是它们具有本质的区别

auto是自动推导变量,推导之后,不能使用auto这个类型进行创建新的变量

而decltype是获取变量的类型,可以使用decltype创建变量

    int x = 10;
    auto y = 10.1;

    decltype(x) z = 11.1;

    cout << y << endl;
    cout << z << endl;

auto推导y一定是一个浮点数类型,而z是一个int

 

三、右值引用

1、左值与右值

左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋
值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左
值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。

总结一句:左值是可以取地址的,左值不一定能够被修改,例如const修饰的变量

右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引
用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能
取地址。右值引用就是对右值的引用,给右值取别名。

右值一般都是:字面量, 表达式返回值, 函数返回值,临时对象也可以被认为是右值

2、关于左值引用、右值引用的问题

1、左值引用可以引用右值吗?

左值是不能直接引用右值的,但是可以在左值引用前面加上const

template<class T>
void Func(const T& x)
{
    
}

x既可以接收左值也可以接收右值

2、右值引用可以引用左值吗?

答案是不能直接引用,但是可以引用move之后的左值

int a = 10;
int&& ra = move(a);

move 本意为 "移动",但该函数并不能移动任何数据,它的功能很简单,就是将某个左值强制转化为右值。

基于 move() 函数特殊的功能,其常用于实现移动语义。

3、右值引用之后的问题

需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可
以取到该位置的地址,也就是说例如:不能取字面量10的地址,但是rr1引用后,可以对rr1取地
址,也可以修改rr1。如果不想rr1被修改,可以用const int&& rr1 去引用。

也就是说右值引用之后,右值就会变成左值,这里与后面完美转发有关

3、移动构造、移动拷贝

1、引用的价值

引用主要是用来减少拷贝,提高效率

左值引用常用的场景:

1、做参数:a、减少拷贝,提高效率  b、做输出型参数

2、做返回值:a、减少拷贝,提高效率 b、引用返回,可以修改返回对象

只有左值引用很难处理以下场景

to_string的返回值是一个string对象,要直接返回它代价较高,如果返回引用,就会出现野指针问题,to_string内部一定会定义一个临时string对象,它出了作用域就会销毁

如果要想提高效率,只能使用输出型参数,传一个string&给to_string,但是是不符合使用习惯的。

C++11的右值引用一个重要功能就是要解决上面的问题。

2、移动构造、移动赋值

在C++11以后,将右值分为两类1、内置类型右值-纯右值 2、自定义类型右值-将亡值

为了更好的说明这个问题,我使用了之前实现的string

namespace ww
{
    class string
    {
    public:
        typedef char *iterator;
        iterator begin()
        {
            return _str;
        }
        iterator end()
        {
            return _str + _size;
        }
        string(const char *str = "")
            : _size(strlen(str)), _capacity(_size)
        {
            // cout << "string(char* str)" << endl;
            _str = new char[_capacity + 1];
            strcpy(_str, str);
        }

        void swap(string &s)
        {
            std::swap(_str, s._str);
            std::swap(_size, s._size);
            std::swap(_capacity, s._capacity);
        }
        // 拷贝构造
        string(const string &s)
            : _str(nullptr)
        {
            cout << "string(const string& s) -- 深拷贝" << endl;
            string tmp(s._str);
            swap(tmp);
        }
        // 赋值重载
        string &operator=(const string &s)
        {

            cout << "string& operator=(string s) -- 深拷贝" << endl;
            string tmp(s);
            swap(tmp);
            return *this;
        }

        ~string()
        {
            delete[] _str;
            _str = nullptr;
        }
        char &operator[](size_t pos)
        {
            assert(pos < _size);
            return _str[pos];
        }
        void reserve(size_t n)
        {
            if (n > _capacity)
            {
                char *tmp = new char[n + 1];
                strcpy(tmp, _str);
                delete[] _str;
                _str = tmp;
                _capacity = n;
            }
        }
        void push_back(char ch)
        {
            if (_size >= _capacity)
            {
                size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
                reserve(newcapacity);
            }

            _str[_size] = ch;
            ++_size;
            _str[_size] = '\0';
        }

        string &operator+=(char ch)
        {
            push_back(ch);
            return *this;
        }
        const char *c_str() const
        {
            return _str;
        }

    private:
        char *_str;
        size_t _size;
        size_t _capacity;
    };

    string to_string(int value)
    {
        bool flag = true;
        if (value < 0)
        {
            flag = false;
            value = 0 - value;
        }
        string str;
        while (value > 0)
        {
            int x = value % 10;
            value /= 10;
            str += ('0' + x);
        }
        if (flag == false)
        {
            str += '-';
        }

        std::reverse(str.begin(), str.end());
        return str;
    }
}

int main()
{
    ww::string str = ww::to_string(-2345);
    return 0;
}

在g++关闭所有优化的条件下,调用to_string会进行两次深拷贝

一次拷贝构造是to_string里面的临时对象拷贝给返回的临时对象,另一次拷贝是临时对象拷贝给在main函数栈帧的str,调用一次to_string就要深拷贝两次,这个代价就十分的巨大了 

这时右值拷贝的作用就体现了,不过它不是直接起作用,而是间接起作用,实现方式就是移动构造和移动拷贝

在STL容器中添加移动构造,移动赋值

所谓的移动构造和移动赋值 ,其实是通过右值引用,因为只有将亡值会走移动构造,移动赋值

而将亡值除了作用域很快就会销毁,我们可以使新的类的指针指向将亡值的数据,将亡值的指针指向新构造的类,然后让将亡值的析构函数来处理新构造类的原始数据,这样就延长了资源的生命周期,而没有改变对象的声明周期。这样就极大的提高了效率,如果没有移动构造移动赋值,只能走拷贝构造,赋值重载,进行深拷贝,代价极大,而移动构造和移动赋值,只是更换指针指向方向,消耗几乎可以忽略不记,例如进行红黑树的拷贝也就成了现实,红黑树如果只进行深拷贝代价十分的大,要深拷贝整棵树,而移动构造和移动赋值可以只更改树根节点指针方向

        // 移动构造
        string(string &&s)
            : _str(nullptr), _size(0), _capacity(0)
        {
            cout << "string(string&& s) -- 移动语义" << endl;
            swap(s);
        }
        // 移动赋值
        string &operator=(string &&s)
        {
            cout << "string& operator=(string&& s) -- 移动语义" << endl;
            swap(s);
            return *this;
        }

只有将亡值才会走移动构造移动赋值,不是将亡值的还会走原来的拷贝构造赋值操作符重载


STL容器,插入接口C++11以后都提供了右值版本,插入过程中,如果传递的对象是右值,那么进行资源转移,减少拷贝

4、完美转发

template<class T>
void Func(T&& x)
{

}
模板中的&&不代表右值引用,而是万能引用,其既能接收左值又能接收右值。
模板的万能引用只是提供了能够接收同时接收左值引用和右值引用的能力,
但是引用类型的唯一作用就是限制了接收的类型,后续使用中都退化成了左值,
void Fun(int &x) { cout << "左值引用" << endl; }
void Fun(const int &x) { cout << "const 左值引用" << endl; }
void Fun(int &&x) { cout << "右值引用" << endl; }
void Fun(const int &&x) { cout << "const 右值引用" << endl; }

template <typename T>
void PerfectForward(T &&t)
{
    Fun(t);
}
int main()
{
    PerfectForward(10); // 右值
    int a;
    PerfectForward(a);            // 左值
    PerfectForward(std::move(a)); // 右值
    const int b = 8;
    PerfectForward(b);            // const 左值
    PerfectForward(std::move(b)); // const 右值
    return 0;
}

所以为了在传递过程中保持它原本的属性(左值,右值),就需要使用到我们的完美转发

 

template <typename T>
void PerfectForward(T &&t)
{
    Fun(forward<T>(t));//使用完美转发,保持属性不变
}

 

5、补充说明

在C++11之前,一个类具有6大默认成员函数
1、构造函数
2、析构函数
3、拷贝构造
4、赋值重载
5、取地址重载
6、const取地址重载
而在C++11以后又增加了移动构造和移动赋值作为默认成员函数
不过移动构造和移动赋值默认生成的条件比较严格
如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任
意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类
型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,
如果实现了就调用移动构造,没有实现就调用拷贝构造。
如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中
的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内
置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋
值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造
完全类似)
如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值

四、关于默认成员函数的关键字

1、final

final关键字已经在继承部分提到了,这里仅仅是简单介绍
final修饰的类不能被继承
class A final
{
private:
    int _a;
};

class B : public A
{
private:
    int _b;
};

 

2、override

override也在前面介绍过了,这里简单过一下,override是放在子类的,用来检查是否重写父类虚函数
class A 
{
public:
    virtual void show()
    {
        cout << _a << endl;
    }
private:
    int _a = 0;
};

class B : public A
{
public:
    virtual void show() override
    {
        cout << _b << endl;
    }
private:
    int _b = 0;
};

 

3、default

C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原
因这个函数没有默认生成。比如:我们提供了拷贝构造,就不会生成移动构造了,那么我们可以
使用default关键字显示指定移动构造生成。
class A 
{
public:
    A(const A& a)
    {
        _a = a._a;
    }

    
private:
    int _a = 0;
};

这时我们使用default强制生成默认构造函数

class A 
{
public:
    A(const A& a)
    {
        _a = a._a;
    }
    A() = default;
    
private:
    int _a = 0;
};

 

 

4、delete

如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁
已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即
可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
delete关键字比较简单,但是引申出的问题还是很多的

1、如何利用delete,定义一个只能在堆上开空间的类?

很显然,只能在堆上定义的类,一定要使用new,使用new就会调用构造函数,所以我们不能够修改构造函数,那么我们就把析构函数delete就可以了,在栈上的对象,出了作用域要销毁,就要调用析构函数,无法调用析构函数,就会报错,可是删除了析构函数,要怎么回收这个类呢?

我们只要自己手动写一个类似析构函数的成员函数,然后手动调用就好了


class A
{
public:
    ~A() = delete;

    A()
    {
        _a = new int[10];
    }

    void destory()
    {
        delete[] _a;
        operator delete(this);
    }

private:
    int *_a;
};

int main()
{
    A a;
    return 0;
}

先来看一下直接在栈上创建对象

 

接下来是在堆上创建对象


class A
{
public:
    ~A() = delete;

    A()
    {
        _a = new int[10];
    }

    void destory()
    {
        delete[] _a;
        operator delete(this);
    }

private:
    int *_a;
};

int main()
{
    A* a = new A();
    a->destory();
    return 0;
}

注意:这里destory函数销毁自己使用的是operator delete ,不可以使用delete,因为delete会自动调用析构函数可是我们已经删除了析构函数,所以不可以使用delete,但是我们可以使用operator delete,它类似于free,不会调用析构函数

 

四、可变参数模板

1、概念

众所周知,在C语言有一个神奇的函数printf(),
这个函数可以传递可变参数,说到“可变”参数,主要是指两点可变:1,参数数量可变;2,参数类
型可变。比如上面演示的C库中的printf,数量是可变的,类型也是可变的。
上面的可变参数是针对函数的,C++11实现了模板也可以是可变参数的
可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技
巧,所以这块还是比较晦涩的。
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}

我们可以使用sizeof来获取模板参数的个数

template<class ...Args>
void showList(Args...args)
{
    cout << sizeof...(args) << endl;
}

int main()
{
    showList(1,2,3,4,5,5,5,6);
    showList();
    showList(1,2);
    return 0;
}

 

2、模板包的展开

模板包的展开,主要有两种
1、递归展开
template <class T, class... Args>
void showList(T value, Args... args)
{
    cout << value << endl;
    showList(args...);
}

//递归终止函数
template <class T>
void showList(T t)
{
    cout << t << endl;
}

int main()
{
    showList(1, 2, 3, 4, 5, 5, 5, 6);
    // showList();
    showList(1, 2, 3);
    return 0;
}
2、逗号表达式
template <class T>
void PrintArg(T t)
{
 cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
 int arr[] = { (PrintArg(args), 0)... };
 cout << endl;
}
int main()
{
 ShowList(1);
 ShowList(1, 'A');
 ShowList(1, 'A', std::string("sort"));
 return 0;
}

3、STL中具体应用

C++11添加可变模板参数,使得所有容器加入了emplace这个函数

在某些场景下emplace的效率要高于insert

对于内置类型没有差别,自定义类型例如像unordered_map这样调用insert要插入键值对,或者其它类型对象的场景,效率更高

拿unordered_map举例,insert要插入一个pair,我们要么创建一个临时对象,或者make_pair, 

总之最后要拷贝两次,一次是创建匿名对象,另一次是插入,而emplace只需要一次就可以
emplace它并不会先生成匿名对象,它会展开可变参数模板包,不停的往下传递,直到最后参数是pair,他只构造最后那一次

五、lambda表达式

lambda表达式书写格式:
[capture-list] (parameters) mutable -> return-type { statement }

1. lambda表达式各部分说明

[capture-list] : 捕捉列表,该列表总是出现在lambda函数的开始位置,编译器根据[]来判断接下来的代码是否为lambda函数捕捉列表能够捕捉上下文中的变量供lambda函数使用
(parameters):参数列表。与普通函数的参数列表一致,如果不需要参数传递,则可以连同()一起省略
mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。
->returntype:返回值类型。用追踪返回类型形式声明函数的返回值类型,没有返回值时此部分可省略。返回值类型明确情况下,也可省略,由编译器对返回类型进行推导
{statement}:函数体。在该函数体内,除了可以使用其参数外,还可以使用所有捕获到的变量。
注意:
在lambda函数定义中,参数列表和返回值类型都是可选部分,而捕捉列表和函数体可以为空。因此C++11中最简单的lambda函数为:[]{}; 该lambda函数不能做任何事情。

2. 捕获列表说明

捕捉列表描述了上下文中那些数据可以被lambda使用,以及使用的方式传值还是传引用
[var]:表示值传递方式捕捉变量var
[=]:表示值传递方式捕获所有父作用域中的变量(包括this)
[&var]:表示引用传递捕捉变量var
[&]:表示引用传递捕捉所有父作用域中的变量(包括this)
[this]:表示值传递方式捕捉当前的this指针
注意:
a. 父作用域指包含lambda函数的语句块
b. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割
比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b,值传递方式捕捉其他所有变量
[&,a, this]:值传递方式捕捉变量a和this,引用方式捕捉其他变量
c. 捕捉列表不允许变量重复传递,否则就会导致编译错误
比如:[=, a]:=已经以值传递方式捕捉了所有变量,捕捉a重复
d. 在块作用域以外的lambda函数捕捉列表必须为空
e. 在块作用域中的lambda函数仅能捕捉父作用域中局部变量,捕捉任何非此作用域或者
非局部变量都会导致编译报错。
f. lambda表达式之间不能相互赋值,即使看起来类型相同

3、lambda表达式底层实现

对于lambda表达式的捕捉列表来说,它的本质实际上是传参
lambda表达式的本质其实是一个仿函数,它的类名是lambda_uuid
所以看似相同的lambda表达式不能够相互赋值

六、包装器

1、包装器

包装器算是C++11在具体后端开发使用的较多的特性

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。

我们可以将函数通过包装器,包装起来,然后放入到map等容器中,根据实际情况来调用

std::function在头文件<functional>
// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;
模板参数说明:
Ret: 被调用函数的返回类型
Args…:被调用函数的形参

int add(int a, int b)
{
    return a + b;
}

struct Func
{
    int operator()(int a, int b)
    {
        return a + b;
    }
};

class Plus
{
public:
    static int addi(int a, int b)
    {
        return a + b;
    }

    double addd(double a, double b)
    {
        return a + b;
    }
};

这里有三种不同的函数,分别演示如何包装

    function<int(int, int)> f1 = add;//没有什么特殊条件直接绑定

    function<int(int, int)> f2 = Func();//仿函数要绑定对象

    function<int(int, int)> f3 = Plus::addi;//静态成员函数指定类域

    function<double(Plus, double, double)> f4 = &Plus::addd;//非静态成员函数需要取地址,并且传类名

2、bind

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器)接受一个可
调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而
言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M
可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺
序调整等操作。
// 原型如下:
template <class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
// with return type (2) 
template <class Ret, class Fn, class... Args>
/* unspecified */ bind (Fn&& fn, Args&&... args);
可以将bind函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对
象来“适应”原对象的参数列表。
调用bind的一般形式:auto newCallable = bind(callable,arg_list);
其中,newCallable本身是一个可调用对象,arg_list是一个逗号分隔的参数列表,对应给定的
callable的参数。当我们调用newCallable时,newCallable会调用callable,并传给它arg_list中
的参数
arg_list中的参数可能包含形如_n的名字,其中n是一个整数,这些参数是“占位符”,表示
newCallable的参数,它们占据了传递给newCallable的参数的“位置”。数值n表示生成的可调用对
象中参数的位置:_1为newCallable的第一个参数,_2为第二个参数,以此类推

3、具体应用

150. 逆波兰表达式求值 - 力扣(LeetCode)

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<long long> st;
        unordered_map<string, function<long long(long long, long long)>> mp = 
        {
            {"+", [](long long x, long long y){ return x + y; }},
            {"-", [](long long x, long long y){ return x - y; }},
            {"*", [](long long x, long long y){ return x * y; }},
            {"/", [](long long x, long long y){ return x / y; }}
        };
        for(const auto& e : tokens)  
        {
            if(mp.count(e))
            {
                long long right = st.top();
                st.pop();
                long long left = st.top();
                st.pop();
                st.push(mp[e](left, right));
            }
            else
            {
                st.push(stoll(e));
            }
        }

        return st.top();
    }
};


总结


例如:以上就是今天要讲的内容,本文仅仅简单介绍了C++11相关特性

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21683.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件测试质量保证与测试

软件测试质量保证与测试 第一章 软件测试概述 1.1 软件测试背景 随着计算机技术的迅速发展和越来越广泛深入地应用于国民经济与社会生活的各个方面&#xff0c;软件系统的规模和复杂性与日俱增&#xff0c;软件的生产成本和软件中存在的缺陷与故障造成的各类损失也大大增加&…

【应用】PostgreSQL 流复制配置

PostgreSQL 流复制配置centos7 安装 postgresql时序库 timescaleDB 的安装postgresql-14 主从流复制主库配置从库配置同步流复制与异步流复制异步流复制转换为同步流复制流复制的相关参数主从流复制原理PostgreSQL WAL 日志主从流复制架构主从流复制的过程基于 docker swarm 的…

CMSC5713-IT项目管理之七、质量管理Quality Management

文章目录7.1 Quality7.2. Software Quality7.2.1. ISO/IEC 25010 Software Qualities7.2.2. Internal versus External qualities7.2.3. Software Metrics7.3. Quality Specification7.4. Project Quality Management7.4.1. Quality Planning7.4.2. Quality Assurance7.4.2.1. …

vscode 阅读 linux kernel 源码

前言 虽然身边的朋友大都在使用 source insight&#xff0c;但我却更喜欢 vscode。 不过 vscode 在代码搜索上确实不如 source insight&#xff0c;这点上我也是吃过亏的。阅读大型代码时&#xff0c;常常搜索不到关键代码&#xff0c;导致对代码的理解不充分。 当使用 vscode…

Java-反射

前言 动态语言与静态语言 动态语言 是一类在运行时可以改变其结构的语言&#xff1a;例如新的函数、对象、甚至代码可以被引进&#xff0c;已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构主要动态语言有&#xff1a;Object…

【开源电路】STM32F401RCT6开发板

【开源电路】STM32F401RCT6开发板&#x1f337;实物PCBA&#xff1a; &#x1f33c;优化后的3D效果图 &#x1f4da;STM32F401RCT6开发板简介 &#x1f4d1;主控是LQFP-64封装的STM32F401RCT6芯片&#xff0c;Micro USB接口供电&#xff0c;功能引脚全部引出&#xff0c;一个…

金融强化学习与finRL开发包

原创文章第110篇&#xff0c;专注“个人成长与财富自由、世界运作的逻辑&#xff0c; AI量化投资”。 01 一些感受 时代的一粒沙&#xff0c;落在每个人身上就是一座山。 这三年&#xff0c;对于这句话&#xff0c;相信很多人更能感同身受。 看历史风云变幻&#xff0c;轻轻…

力扣(LeetCode)21. 合并两个有序链表(C++)

迭代 同时遍历两个链表 &#xff0c; 当前结点值较小的结点插入新的链表尾部。直到有一个链表为空 &#xff0c; 我们将另一个非空链表插入新的链表尾部。 提示 : 使用哑结点&#xff0c;避免特判头结点。二路归并思想应用于链表~ class Solution { public:ListNode* mergeT…

gRPC gateway - Http Restful API gRPC 协议转换

gRPC gateway - http restful gRPC gateway 介绍 gRPC-Gateway 是protocalBufffer的编译插件,根据protobuf的服务定义自动生成反向代理服务器&#xff0c;并将Restful Http API 转化为 gRPC通信协议。反向代理服务器根据 google.api.http 注解生成。 gRPC底层是使用HTTP2 协…

mybatis # $

总结&#xff1a; # 你传入的变量类型会被保留 $ 本质就是拼接 不会考虑拼串的 $ 情况下 参数是整数 跟参数是字符串 字符串情况&#xff1a; 缺少’’ 相当于字符串拼接 ”select * from t_user where username “ “张三” ”select * from t_user where username 张三&…

java 容器

java 容器 数组 数组的扩容问题 ArrayList 的默认初始化容量为0&#xff0c;首次添加元素时&#xff0c;创建容量为&#xff08;10 || 添加集合大小) ,以后每次扩容的话&#xff0c;为当前容量的1.5倍 public ArrayList() {/*初始化容量大小为0private static final Object…

CEAC之《计算机应用助理工程师》2

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;微微的猪食小窝 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! 本文由 微微的猪食小窝 原创 收录于专栏 【CEAC证书】 1组合框的常用属性有 ____________ 。 A、Index B、Text C、Caption D、ListCountA,B,D2在…

ES6 入门教程 16 Reflect 16.2 静态方法 16.3 实例:使用 Proxy 实现观察者模式

ES6 入门教程 ECMAScript 6 入门 作者&#xff1a;阮一峰 本文仅用于学习记录&#xff0c;不存在任何商业用途&#xff0c;如侵删 文章目录ES6 入门教程16 Reflect16.2 静态方法16.2.1 Reflect.get(target, name, receiver)16.2.2 Reflect.set(target, name, value, receiver)1…

数据结构之:数组

数组初体验之数组中重复的数字 数组 &#xff1a; 有限个 相同类型的变量 组成的有序集合 int[] arr; int arr[]; // 静态初始化 String[] strArr {"和平精英","王者荣耀","开心消消乐","欢乐斗地主"}; // 动态初始化 String[] strAr…

自学 TypeScript 第三天 使用webpack打包 TS 代码

安装&#xff1a; 首先第一步&#xff0c;我们要初始化我们项目&#xff0c;在目录下输入 npm init 接下来&#xff0c;我们的安装几个工具 npm i -D webpack webpack-cli typescript ts-loader -D 意思是 开发依赖&#xff0c;也就是我们现在所安装的依赖都是开发依赖&am…

知乎 日报

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言评论界面首页cell的小标题的文字显示下拉刷新前言 这周完成了评论内容&#xff0c;改了一些小bug。收藏界面正在加油&#xff0c;FMDB库目前不是很理解 评论界面…

【C++初阶】三、类和对象(中)

在上一篇类和对象中&#xff0c;我们初步了解了类和对象的基本概念&#xff0c;知道了什么是类&#xff0c;接下来一起看看类和对象的具体细节和实现吧。&#xff08;以日期类举例&#xff09; 文章目录类和对象【中】1.类的6个默认成员函数2. 构造函数2.1 构造函数定义2.2 构造…

SpringBoot SpringBoot 开发实用篇 4 数据层解决方案 4.16 SpringBoot 整合 ES 客户端操作

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇4 数据层解决方案4.16 SpringBoot 整合 ES 客户端操作4.16.1 环境准备…

[附源码]java毕业设计停车场收费管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

DP入门(一)

前言&#xff1a;由于作者经常卡力扣周赛最后一题的dp&#xff0c;因此决定痛改前非&#xff0c;从头做人&#xff0c;争取下次能做出最后一道dp ak周赛&#xff01;呜呜呜加油~~ 因此 这个系列的文章不会教 dp &#xff0c;只会讲刷题思路&#xff0c;目前的计划是先更 lc 的题…