当前位置: 首页 > news >正文

C++11

统一的列表初始化

在介绍这里的列表初始化之前,首先我认为这是一个比较鸡肋的功能特性,而且使用起来会和之前C++98时有些区别。

 // 首先可以定义普通的内置类型的变量int x1 = 1;int x2 = { 1 };int x3{ 1 }; // 这样看起来着实有些怪int arry1[] = { 1,2,3,4,5 };int arry2[]{ 1,2,3,4,5 }; // 数组还可以这样定义

如果是自定义类型,定义可以如下:

struct Point
{int _x;int _y;
};
int main()
{Point p1 = { 1,2 };Point p2{ 2,2 };   
}

上面的定义更多看起来兼容了C语言,如果有一个类,成员为私有成员,并且有自己的构造函数,那么这个时候使用花括号其实就是在调用这个类的构造函数:

class Date
{
private:int _hour;int _minute;int _second;
public:Date(int x = 0, int y = 0, int z = 0):_hour(x),_minute(y),_second(z){}void Print(){cout << _hour << " " << _minute << " " << _second;}
};
int main()
{Date d1{ 10,20,30 };d1.Print();return 0;
}

在定义vector的时候可以如下定义:

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

但是我们之前在学习vector的时候并没有看到过类似第二行的初始化方式,所以这里C++11是如何实现这种方式的?

C++11为容器提供了一个初始化的方式:initializer_list,它作为构造函数的参数,C++11为STL中的不少容器添加了initializer_list作为参数的构造函数,初始化对象就方便了很多。

在这里插入图片描述

initializer_list的成员函数中,begin返回指向第一个位置的指针,end返回指向最后一个位置的下一个位置的指针(nullptr)。具体来说就是initializer_list封装了一个常量数组,然后支持迭代器返回数组的begin和end以方便对其进行遍历。

decltype

之前我们使用typeid可以拿到元素的类型的字符串,但是这似乎是一个很鸡肋的功能,因为它除了能打印什么都做不了。

	auto il = { 1,2,3,4,5 };cout << typeid(il).name() << endl;

但是decltype的作用就不一样了,可以参考下面这个例子:

// decltype的一些使用场景
template<class T1, class T2>
void F(T1 t1, T2 t2)
{decltype(t1 * t2) ret = t1 * t2;cout << typeid(ret).name() << endl;
}
void Func()
{F(1.2, 2);
}

最后打印的结果是double

其次这里提一下,nullptr和范围for都是C++11新增的内容,nullptr解决了之前NULL被定义成字面量0的问题,范围for想必大家都很熟悉了,这里就不多赘述了。

STL中的一些变化

C++11在STL中添加了一些新容器并且对已有容器增加了一些新的接口函数。

除掉我们熟知的unordered_map和unordered_set,有两个容器这里需要介绍一下:array和forward_list

array是一个固定大小的容器,和vector相比,vector是动态的数组。array对比的主要是静态数组,那么与静态数组相比,优势在哪儿呢?

之前我们知道系统对普通数组的越界检查是一种抽查,只能检查到临界位置的一些元素是否有越界问题,array这里就对这个问题做出了很好的解决;但是它任然是一个很鸡肋的容器,并不会对容器中的元素进行初始化。

forward_list容器是一个单链表,容器并没有提供尾插和尾删,因为找尾的时间复杂度是很高的,对比list,唯一的优点就是每一个节点节省一个指针,但是用起来真的差list好远

在vector容器中,增加了cbegin和cend以及shrink_to_fit三个接口,cbegin和cend返回const迭代器,二shrink_to_fit是一个缩容接口,一般情况都是异地缩容,一般不要使用这个接口,因为这个接口的使用会导致拷贝。在容器中还提供了许多右值引用相关的接口,之后再谈。

在我之前的博客中继承和多态部分提到的final和override也是C++11新增的部分,如果一个虚函数不想被重写,可以加final修饰,也可以修饰一个类,这个类叫最终类,override是一个检查子类虚函数是否完成重写

右值引用

在引入右值引用之前,先来谈一下什么是左值什么是右值

左值是一个数据表达式,说人话就是可以获取它的地址,也可以对其进行赋值,左值是可以出现在复制符号的右边的,但是右值不可以出现在赋值符号的左边

左值引用就是给左值起别名,左值引用也可以引用右值,但是必须是const左值引用。

右值也是一个数据表达式,如字面常量,表达式返回值,函数返回值(传值返回)等;右值不能取地址

右值引用不可以引用左值,但是如果**std::move(左值)**就可以被右值引用所引用

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);
}

所以右值引用究竟有什么用呢?和const左值引用有什么区别?

引用出现的目的就是为了减少拷贝,主要使用在函数传参,我们之前的学习中使用引用传参是非常舒服的。但是如果我们想要传递一个参数作为返回值,就有些麻烦了,在栈上创建的内存出栈后会被销毁,如果在堆上开辟空间又需要对空间进行回收,在C++11之前解决问题的方法就是输出型参数,在传递进去的参数就是要改变的变量的引用或地址,但是用起来总感觉怪怪的,所以今天的右值引用就很好的解决了这个问题。

template<class T>
T&& func(const T& x)
{T ret;// ...return ret;
}

如果类似上面的这种写法,是会报错的,ret出了函数任然会被销毁,来看下面这个例子:

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()
{string str = to_string(1234);return 0;
}

这是一个传值返回的函数,这里会造成两次拷贝构造,编译器经过优化后只有一次拷贝构造.

在传递参数返回值的时候,如果返回值不是很大,会将返回值经过一次拷贝后返回寄存器中;如果返回值比较大,在main函数的函数栈帧边缘上开辟一段空间进行存放。

编译器在类似上面的这种写法,可以直接进行优化,只进行一次构造,不会生成中间变量。

但是如果这么写呢?

int main()
{string str;str = to_string(1234);return 0;
}

这样写VS编译器就不会再做出优化了,产生临时变量,进行两次拷贝构造。

C++11对右值又分为纯右值和将亡值,将亡值顾名思义就是即将要被销毁的值,上面函数中我们所在函数栈帧中创建的变量str,就算有编译器优化,也需要最少一次拷贝构造,那么右值引用的意义就是为了减少拷贝构造所带来的消耗。

在拷贝构造部分提供一个右值引用的版本:

// 移动构造
string(string&& s):_str(nullptr)
{cout << "string(const string& s) -- 移动拷贝" << endl;swap(s);
}
// 拷贝构造
string(const string& s):_str(nullptr),_capacity(0),_size(0)
{cout << "string(const string& s) -- 深拷贝" << endl; string tmp(s._str);swap(tmp);
}

通过代码区分一下移动拷贝和拷贝构造,移动构造将this指向的对象和s进行交换,可以理解为,函数栈帧中创建的且要返回的值是一个将亡值,所以它匹配移动构造函数,进行资源转移。

那么在有了移动构造之后,编译器在没有进行优化时就会进行一次拷贝构造和一次移动构造;在优化之后,会直接移动构造,并且将返回值直接识别为将亡值,这一切都是编译器做的。

赋值运算符重载也是一样的,除了实现拷贝复制,还可以实现移动赋值:

// 拷贝赋值
string& operator=(const string& s1)
{cout << "string& operator = (string s) -- 拷贝赋值" << endl;if (this == &s1){return *this;}delete[] _str;_str = new char[s1._capacity + 1];strcpy(_str, s1._str);_capacity = s1._capacity;_size = _capacity;return *this;
}
// 移动赋值
string& operator=(string&& s)
{cout << "string& operator = (string s) -- 移动赋值" << endl;swap(s);return *this;
}

所以移动构造的使用几乎是我们察觉不到的,在设计类的时候设计对应的函数即可,编译器会帮我们做好这一切相关的工作的。右值引用和左值引用减少拷贝的原理还不太一样,左值引用是取别名直接起作用,右值引用是间接起作用,实现移动构造和移动赋值,在拷贝的场景中,如果是右值(将亡值),转移资源。

另外就是右值的插入,如果要插入一个匿名对象,在没有移动拷贝的时候,进行的就是深拷贝,有移动拷贝的情况下就节省了空间。

右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定的位置,且可以取到该位置地址;比如不能取字面常量10的地址,但是字面常量被rr1引用后,可以对rr1取地址,也可以修改rr1,可以理解为rr1指向的不是10本身,修改rr1并不会影响10这个字面常量,用const修饰rr1就可以防止其被修改

右值引用本身是左值。我们之前模拟实现过的list如果也想支持C++11,就要注意一个问题,当参数作为右值引用传入进来进行资源转移后,再将参数继续传下去(在函数中继续使用该参数调用下一个函数),就无法再保持参数的右值属性了

看下面这个例子:

// 万能引用
template<typename T>
void func1(T&& t)
{cout << "func1(T&& t)" << endl;
}
void func2(int& x)
{cout << "func2(int&)" << endl;
}
void func3(int&& x)
{cout << "func3(int&&)" << endl;
}
int main()
{int x = 10;func2(x);// func3(x);(会编译报错,右值引用无法引用一个左值)func3(10);func1(x);func1(10);return 0;
}

通过上面的代码我们可以发现,在普通函数的调用中,右值引用是不可以传递一个左值进去的;但是在模板函数中,是可以这么做的,这里能实现是发生了引用折叠(&&->&),更离谱的是,这里还可以传递const左值和const右值!但是带const的传值是不可以对参数进行修改的。

右值引用在一些情况下会有一些问题:

void func(int& x)
{	cout << "func(int&)" << endl;	}
void func(const int& x)
{	cout << "func(const int&)" << endl;	}void func(int&& x)
{	cout << "func(int&&)" << endl;	}
void func(const int&& x)
{	cout << "funcconst int&&)" << endl;	}template<typename T>
void func1(T&& t)
{func(t);
}
int main()
{func1(10);int a = 0;func1(a);func1(move(a));const int b = 9;func1(b);func1(move(b));return 0;
}

最后运行的结果如下:

在这里插入图片描述

可以理解,所有的参数无论是左值还是右值,在传入func1函数后,t实际上有左值属性,所以在掉func函数的时候,就相当于左值调用,所以才有上面的打印。

这里就需要认识一个新的名词:完美转发

template<typename T>
void func1(T&& t)
{// 完美转发 保持属性func(std::forward<T>(t));
}

在更改过代码后,再运行试试:

在这里插入图片描述

类的变化

移动构造和移动赋值

在上面我们提到了基于右值的移动构造和移动赋值,C++11中提供了默认生成的移动构造和移动赋值,但是有一些需要注意的点:

如果没有自己实现移动构造函数,且没有实现析构函数,拷贝构造,拷贝赋值重载中的任意一个,那么编译器就会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。

默认移动赋值和上面的移动构造完全类似

如果提供了移动构造和移动赋值,编译器不会自动提供拷贝构造和拷贝赋值

类成员变量初始化

C++11允许类在定义时给成员变量初始缺省值,默认构造函数会使用这些缺省值初始化

default和delete关键字

默认生成的函数是有条件的,有些情况下,函数可能会因为某些原因没有默认生成,比如提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。

class Person
{public:Person(const char* name = "", int age = 0):_name(name),_age(age){}Person(const Person& x):_name(x._name())_age(x._age){}Person(Person&& p) = default;private:mystring::string _name;int _age;
};

delete关键字和default关键字正好相反,delete可以禁止生成默认函数,用法也是相似的。

lambda表达式

首先来说一下lambda表达式的应用场景,C++为了减少使用函数指针的使用(函数指针确实是一个很烦人的东西),就有了仿函数,之后C++11认为仿函数也不是那么好用,于是就有了lambda表达式,lambda本质就是一个可调用的对象,之后我也会多次提到。

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

这个书写表达式一看一脸懵,第一部分[capture-list]是必须要写的;第二部分是参数 (parameters) 没有参数可以不写;第三部分 mutable 一般不需要;第四部分是返回值 -> return-type ,一般都不写,会自动推导;最后一个部分是函数体,必须要写 { statement }

// 进行int对象比较的lambdaauto compare = [](int x, int y)->bool {return x > y; };
// 也可以写成下面这样:auto compare = [](int x, int y){return x > y};

lambda本质是一个可调用的对象,返回值auto是因为返回值是编译器生成的,我们不知道返回值究竟是什么。

前面我只介绍了lambda表达式的语法组成,但是各个部分究竟怎么用:

	auto compare = [](int x, int y)->bool {return x > y; };

compare可以看作是一个对象,这里可以给compare传递两个变量来进行大小的判断,那么已有变量可不可以不进行传递直接使用呢?

那就提到第一个部分:[capture-list]:捕捉列表,编译器根据 [] 这个符号来判断接下来的代码是否为lambda函数,同时捕捉列表可以捕捉上下文中的变量供lambda函数使用。如何使用?

int a = 0;
int b = 1;
auto add2 = [b](int x) {return x + b;};
cout << add2(a);

上面的代码中,在捕捉列表中捕捉变量b,参数传递就可以只传一个。但是这里不能传递add2函数之后所创建变量。

在这里插入图片描述

第二条中[=]的父作用域是函数体外的变量,由于编译器由上到下扫描,所以这里父作用域中的变量是不包括lambda函数之后所创建的变量的。

如果这里想要写一个lambda函数来交换两个变量,怎么做?

int a = 0;
int b = 1;
auto swap = [a, b]()
{int tmp = a;a = b;b = tmp;
};

这里是无法编译通过的,捕捉列表中的的变量本质上是拷贝过来的,并且增加了const属性,这也就是第三部分mutable的作用

在这里插入图片描述

编译并未报错,但是没有真的交换变量,所以验证了捕捉列表中的变量是拷贝。

在这里插入图片描述

将捕捉列表中的值改为引用就可以对变量进行交换了。

之前提到了lambda表达式本质上是实现了一个对象,这里我们对lambda表达式进行底层的分析:

在这里插入图片描述

在底层中,编译器会自动帮我们生成一个类;call部分就是定义构造函数,可以发现构造了一个什么类是编译器自己决定的,这也就是为什么返回值中我们要使用auto。

可变参数模板

在C++11中引入了一个新的特性:可变参数模板

在C语言中就有可变参数:printf函数第一个参数为format,第二个参数就是可变参数,也就是说不确定有几个参数。

在这里插入图片描述

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

Args是一个模板参数包,args是一个函数形参参数包;在STL的很多容器中也支持插入参数包:

在这里插入图片描述

看一下可变参数模板的使用:

void ShowList()
{cout << endl;
}
// args参数包可以接收0-N个参数
template <class T, class ...Args>
void ShowList(T val, Args... args)
{cout << val << " ";ShowList(args...);
}int main()
{ShowList(1);ShowList(1, 1.1);ShowList(1, 1.1, string("xxxxxx"));return 0;
}

上面的代码中,ShowList函数中还有一个参数val,他的类型是T,也就将参数包中的参数一个一个传递给val,然后再执行函数,最后提供一个没有参数的ShowList函数。

在这里插入图片描述

上面是C++容器和线程库中的一些函数对可变参数模板的使用,可变参数模板的底层晦涩难懂,但是用起来还是很好的。

这里举一个list的例子:

list<int> list1;
list1.push_back(1);
list1.emplace_back(2);
list1.emplace_back();
for (auto e : list1)
{cout << e << " ";
}
cout << endl;

上面的代码中,list调用emplace_back和调用普通的push_back没有区别,当调用emplace_back的时候,如果没有传递参数,默认构造一个值为0的对象。

list<pair<int, char>> mylist;
mylist.push_back(make_pair(1, 'a'));
mylist.emplace_back(2, 'b');

如果list中的元素是pair类型的,就可以不写make_pair而直接使用参数包讲参数写入;在第二行中调用make_pair构造一个对象后再通过拷贝构造或移动构造将值插入到list中,而调用emplace_back则是直接通过递归调用直接构造。

在这里插入图片描述

可以发现emplace_back对左值没有任何优化空间,在实现了移动构造后。emplace_back减少的只是一次浅拷贝,所以emplace_back并没有想象中那么高效;但没有实现移动构造,差距就很大了。

包装器

如果我们想要实现一个让两个int类型的值相加的函数,那么实现的方法有很多种:

int f(int a, int b)
{return a + b;
}
struct Functor
{
public:int operator()(int a, int b){return a + b;}
};
class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};

上面实现了一个普通函数,一个仿函数,和一个类,类中提供了int类型变量的相加(但函数是静态的)和一个double类型变量的相加;不同的函数调用起来有不同的方法,那么包装器的作用就是将这些不同类型的函数进行包装,使其调用统一。

使用包装器需要添加头文件functional:

#include <iostream>
#include <functional>using namespace std;int f(int a, int b)
{return a + b;
}
struct Functor
{
public:int operator()(int a, int b){return a + b;}
};
class Plus
{
public:static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};int main()
{function<int(int, int)> func1(f);cout << func1(1, 2);function<int(int, int)> func2 = Functor();cout << " " << func2(1, 2);function<int(int, int)> func3 = [](const int a, const int b) { return a + b; };// 静态成员函数的包装function<int(int, int)> func4 = Plus::plusi; // 类成员函数function<int(Plus, int, int)> f6 = &Plus::plusd;cout << f6(Plus(), 1, 2) << endl;Plus plus;function<int(int, int)> f7 = [&plus](int x, int y)->int {	return plus.plusd(x, y);  };return 0; 
}

包装器的本质是一个类模板:

在这里插入图片描述

Ret是被调用函数的返回类型,Args…是被调用函数的形参;知道这些,相信大家对上面func1和func2都可以很好地理解,func3说明包装器还可以包装lambda表达式

func4和func5是对类内成员的包装,可以发现包装这些函数的时候和包装普通函数不太一样,在赋值的时候,需要指定类域,如果这个函数是静态函数,类域前可以加取地址符也可以不加,但是如果不是静态函数,必须加取地址符,另外就是在参数部分要在第一个参数之前添加类域这个参数。在调用这些被包装的类内的函数的时候,需要在调用的时候传递类对象,因为调用类内的函数需要this指针

func7则是使用lambad的捕获列表对类对象进行捕获,这就说明,其实包装器本身更在意的是函数参数和返回值是否正确。

bind

在包装器部分还有一个知识点是绑定,绑定可以交换传入的两个函数的参数,其次绑定也可以固定绑定参数。

int Plus(int x, int y)
{return x - y;
}class Sub
{
public:int sub(int a, int b){return a + b;}
};
int main()
{function<int(int, int)> func1 = bind(Plus, placeholders::_1, placeholders::_2);cout << func1(1, 2) << endl;// 可以调整参数位置function<int(int, int)> func2 = bind(Plus, placeholders::_2, placeholders::_1);cout << func2(1, 2) << endl;function<bool(int, int)> func3 = bind(less<int>(), placeholders::_1, placeholders::_2);// 固定绑定参数function<int(Sub, int, int)> func4 = &Sub::sub;cout << func4(Sub(), 2, 4) << endl;function<int(int, int)> func5 = bind(&Sub::sub, Sub(), placeholders::_1, placeholders::_2);cout << func5(10, 20) << endl;return 0;
}

可以将绑定函数看作是一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表

在参数部分,placeholders_1就是第一个参数,placeholders_2就是第二个参数并以此类推。

固定绑定参数就例如上面代码中,如果包装一个类中的对象,就需要在调用的时候加一个对象,通过对象去调用,在bind绑定参数后可以直接进行调用。

相关文章:

C++11

C11 统一的列表初始化 在介绍这里的列表初始化之前&#xff0c;首先我认为这是一个比较鸡肋的功能特性&#xff0c;而且使用起来会和之前C98时有些区别。 // 首先可以定义普通的内置类型的变量int x1 1;int x2 { 1 };int x3{ 1 }; // 这样看起来着实有些怪int arry1[] { 1,…...

ubuntu18.04 配置zlmediakit 支持ffmpeg转码记录

1、zlmediakt 默认不支持ffmepg转码&#xff0c;需要在根目录下的CamkeLists.txt里面option(ENABLE_FFMPEG "Enable FFmpeg" OFF) 将OFF改成ON, 删除原有的build目录&#xff0c;sudo mkdir build. cd build,cmake .. 这样在编译生成文件夹release/linux/debug/生…...

H68K配置路由功能

系统环境Armbian ubuntu系统 参考 如何使用Debian/Ubuntu等Linux做软路由&#xff08;物理机版本&#xff0c;非虚拟机容器版&#xff09; - 知乎 https://zhuanlan.zhihu.com/p/587068225 按照他操作的结果,就是只有一个网卡正常 最后一顿操作就出现了我这么个配置 更新源…...

*2.5 迭代法的收敛阶与加速收敛方法

学习目标&#xff1a; 了解迭代法的基本概念和原理。学习者需要理解迭代法的基本概念和原理&#xff0c;包括迭代过程、迭代格式、收敛性等基本概念。 熟练掌握迭代法的收敛阶和收敛速度。学习者需要了解迭代法的收敛阶和收敛速度&#xff0c;掌握如何计算迭代法的收敛阶和收敛…...

仪表板展示 | X-lab开放实验室GitHub开源项目洞察大屏

背景介绍 X-lab开放实验室是一个开源软件产业开放式创新的共同体&#xff0c;由来自国内外著名高校、创业公司、部分互联网与IT企业的专家学者与工程师所构成&#xff0c;目前已在包括开源治理标准制定、开源社区行为度量与分析、开源社区流程自动化、开源全域数据治理与洞察等…...

【c语言】五大内存区域 | 堆区详解

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 给大家跳段街舞感谢支持&#xff01;ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…...

【JavaScript】动态表格

&#x1f38a;专栏【 前端易错合集】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 大一同学小吉&#xff0c;欢迎并且感谢大家指出我的问题&#x1f970; &#x1f354;介绍 就是在输入框中输入数字后&#xff0c;再按…...

Css如何优雅的实现抽奖转盘

如图&#xff0c;抽奖转盘&#xff0c;可以拆分为几部分&#xff1a; 1.底部大圆&#xff1b; 2.中间小圆&#xff1b; 3.扇形区&#xff1b; 4.扇形内部奖品区&#xff1b; 5.抽奖按钮&#xff1b; 6.点击抽奖按钮时旋转动效及逻辑&#xff1b; 这其中&#xff0c;扇形区&am…...

在Java的小问题

问题1&#xff1a;如何在Java中创建一个对象&#xff1f; 解决方法&#xff1a; 在Java中&#xff0c;要创建一个对象&#xff0c;需要以下步骤&#xff1a; 创建一个类&#xff0c;定义对象的属性和行为。在类中定义一个构造函数&#xff0c;用于初始化对象的属性。在程序中…...

HashMap的扩容机制、初始化容量大小的选择、容量为什么是2的次幂

前置知识 先来看看HashMap中的成员属性 解释&#xff1a; size当前的容器中Entry的数量&#xff0c;也就是当前K-V的数量loadFactory装载因子&#xff0c;用来衡量HashMap满的程度&#xff0c;loadFactory的默认值是0.75threshold临界值&#xff0c;当实际KV数量超过threshol…...

[jenkins自动化2]: linux自动化部署方式之流水线(下篇)

目录 1. 引言: 2. 进阶操作 流水线 -> 2.1 简介: -> 2.2 最终效果图展示: -> 2.3 有没有心动, 真的像流水线一样, 实现了一键部署启动 3. 实现方式 3.1 下载几个插件 3.2 创建流水线任务 3.3 点击配置 3.4 根据流水线语法 写一个简单的helloworld 3.5 执行…...

idea使用 ( 二 ) 创建java项目

3.创建java项目 3.1.创建普通java项目 3.1.1.打开创建向导 接 2.3.1.创建新的项目 也可以 从菜单选择建立项目 会打开下面的选择界面 3.1.2.不使用模板 3.1.3.设置项目名 Project name : 项目名 Project location : 项目存放的位置 确认创建 3.1.4.关闭tips 将 Dont s…...

RabbitMq-接收消息+redis消费者重复接收

在接触RammitMQ时&#xff0c;好多文章都说在配置中设置属性 # rabbitmq 配置 rabbitmq:host: xxx.xxx.xxx.xxxport: xxxxusername: xxxpassword: xxxxxx## 生产端配置# 开启发布确认,就是confirm模式. 消费端ack应答后,才将消息从队列中删除#确认消息已发送到队列(Queue)pub…...

Orangepi Zero2 全志H616简介

为什么学 学习目标依然是Linux 系统 &#xff0c;平台是 ARM 架构 蜂巢快递柜&#xff0c;配送机器人&#xff0c;这些应用场景用C51,STM32单片机无法实现 第三方介入库的局限性&#xff0c;比如刷脸支付和公交车收费设备需要集成支付宝SDK&#xff0c;提供的libalipay.so 是…...

Golang每日一练(leetDay0047)

目录 138. 复制带随机指针的链表 Copy List with Random-pointer &#x1f31f;&#x1f31f; 139. 单词拆分 Word Break &#x1f31f;&#x1f31f; 140. 单词拆分 II Word Break II &#x1f31f;&#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &…...

HCL Nomad Web 1.0.7发布和新功能验证

大家好&#xff0c;才是真的好。 要问在HCL Notes/Domino系列产品中&#xff0c;谁更新得最快&#xff0c;那么答案一定是HCL Nomad Web。 你看上图右边&#xff0c;从1.0.1更新到1.0.7&#xff0c;都没花多少时间。 从HCL Nomad Web 1.0.5版本开始&#xff0c;可以支持直接…...

春招网申简历填写三技巧

网申第一关很重要&#xff0c;不夸张的说网申决定了你的笔试机会&#xff0c;从如信银行考试中心了解到&#xff0c;银行网申筛选过程中&#xff0c;有机器筛选人工筛选两道程序&#xff0c;掌握填写技巧后对提升简历通过率有较大帮助&#xff0c;一定要把握住&#xff0c;关于…...

计算机网络基础知识总结

经过学习我们可以知道: 关于计算机网络: ip地址端口号协议协议分层TCP五层协议协议封装两台计算机之间的通信 目录 ip地址 端口号 协议 协议分层 五层协议体系结构 (1) 应用层 (2) 运输层 (3) 网络层 (4)数据链路层 (5)物理层 封装&分用 两台主机之间的通信 …...

(下)苹果有开源,但又怎样呢?

一开始&#xff0c;因为 MacOS X &#xff0c;苹果与 FreeBSD 过往从密&#xff0c;不仅挖来 FreeBSD 创始人 Jordan Hubbard&#xff0c;更是在此基础上开源了 Darwin。但是&#xff0c;苹果并没有给予 Darwin 太多关注&#xff0c;作为苹果的首个开源项目&#xff0c;它算不上…...

row_number 和 cte 使用实例:考场监考安排

row_number 和 cte 使用实例&#xff1a;考场监考安排 考场监考安排使用 cte 模拟两个表的原始数据使用 master..spt_values 进行数据填充优先安排时长较长的考试使用 cte 安排第一个需要安排的科目统计老师已有的监考时长尝试使用 cte 递归&#xff0c;进行下一场考试安排&…...

2023天梯赛记录

文章目录 L2-001 紧急救援L2-002 链表去重L2-004 这是二叉搜索树吗&#xff1f;L2-005 集合相似度L2-006 树的遍历L2-007 家庭房产L2-010 排座位L2-011 玩转二叉树L2-012 关于堆的判断L2-013 红色警报L2-014 列车调度L2-016 愿天下有情人都是失散多年的兄妹L2-019 悄悄关注L2-0…...

被吐槽 GitHub仓 库太大,直接 600M 瘦身到 6M,这下舒服了

大家好&#xff0c;我是小富&#xff5e; 前言 忙里偷闲学习了点技术写了点demo代码&#xff0c;打算提交到我那 2000Star 的Github仓库上&#xff0c;居然发现有5个Issues&#xff0c;最近的一条日期已经是2022/8/1了&#xff0c;以前我还真没留意过这些&#xff0c;我这人懒…...

OpenGL(三)——着色器

目录 一、前言 二、Shader 2 Shader 2.1 顶点着色器 2.2 片段着色器 三、APP 2 Shader 四、顶点颜色属性 五、着色器类C 一、前言 着色器Shader是运行在GPU上的小程序&#xff0c;为图形渲染管线的某个特定部分而运行。各阶段着色器之间无法通信&#xff0c;只有输入和输…...

【MySQL】单表查询

一、表的准备 查询操作的SQL演示将基于下面这四张表进行&#xff0c;我们先创建好这四张数据表&#xff0c;并为其添加数据。 1、第一张表为部门表&#xff0c;名称为包含三个字段&#xff1a;部门编号&#xff08;deptno&#xff09;&#xff0c;部门名称&#xff08;dname&…...

第一章 安装Unity

使用Unity开发游戏的话&#xff0c;首先要安装Unity Hub和Unity Editor两个软件。大家可以去官方地址下载&#xff1a;https://unity.cn/releases/full/2020 &#xff08;这里我们选择的是2020版本&#xff09; Unity Hub 是安装 Unity Editor、创建项目、管理帐户和许可证的主…...

20230425----重返学习-vue项目-vue自定义指令-vue-cli的配置

day-057-fifty-seven-20230425-vue项目-vue自定义指令-vue-cli的配置 vue项目 vuex版 普通版纯axios&#xff1a;切换页面&#xff0c;就会重新发送一次ajax请求普通版升级&#xff1a;vuex版vuex的常用功能 vuex 数据通信vuex 缓存数据 前进后退&#xff0c;切换页面&#…...

el-input 只能输入整数(包括正数、负数、0)或者只能输入整数(包括正数、负数、0)和小数

使用el-input-number标签 也可以使用typenumbe和v-model.number属性&#xff0c;两者结合使用&#xff0c;能满足大多数需求&#xff0c;如果还不满足&#xff0c;可以再结合正则表达式过滤 <el-input v-model.number"value" type"number" /> el-i…...

Docker Compose的常用命令与docker-compose.yml脚本属性配置

Docker Compose的常用命令与配置 常见命令ps&#xff1a;列出所有运行容器logs&#xff1a;查看服务日志输出port&#xff1a;打印绑定的公共端口build&#xff1a;构建或者重新构建服务start&#xff1a;启动指定服务已存在的容器stop&#xff1a;停止已运行的服务的容器&…...

with语句和上下文管理器(py编程)

1. with语句的使用 基础班向文件中写入数据的示例代码: # 1、以写的方式打开文件f open("1.txt", "w")# 2、写入文件内容f.write("hello world")# 3、关闭文件f.close()代码说明: 文件使用完后必须关闭&#xff0c;因为文件对象会占用操作系统…...

《JavaEE初阶》HTTP协议和HTTPS

《JavaEE初阶》HTTP协议和HTTPS 文章目录 《JavaEE初阶》HTTP协议和HTTPSHTTP协议是应用层协议:使用Fiddler抓取HTTP请求和响应:Fiddler的下载和基本使用:Fiddler的中间代理人身份:其他抓包工具: 先简单认识HTTP请求与HTTP响应:HTTP请求:HTTP响应: HTTP请求详解:首行&#xff1…...