网站制作多少钱/可以看国外网站的浏览app
目录
前言
一、C++11的简介
二、C++11的小故事。
三、统一的列表初始化
1.列表初始化
2.initializer_list
四、右值引用
1.什么是左值
2.什么是右值
3.右值引用写法
4.右值的分类
5.右值引用的作用
6.STL容器中的右值引用
7.万能引用
总结
前言
C++11相较于之C++98,能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多。许多功能看起来会很怪,效果却很好,所以我们要作为一个 重点去学习。
一、C++11的简介
在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了 C++98称为C++11之前的最新C++标准名称。不过由于C++03(TC1)主要是对C++98标准中的漏洞 进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98/03标准。 从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。
二、C++11的小故事。
1998年是C++标准委员会成立的第一年,本来计划以后每5年视实际需要更新一次标准,C++国际 标准委员会在研究C++ 03的下一个版本的时候,一开始计划是2007年发布,所以最初这个标准叫 C++ 07。但是到06年的时候,官方觉得2007年肯定完不成C++ 07,而且官方觉得2008年可能也 完不成。最后干脆叫C++ 0x。x的意思是不知道到底能在07还是08还是09年完成。结果2010年的 时候也没完成,最后在2011年终于完成了C++标准。所以最终定名为C++11。
三、统一的列表初始化
1.列表初始化
在C++98中,标准允许使用花括号{}对数组或者结构体元素进行统一的列表初始值设定,如下
C++11提出一切都可以使用列表初始化。如下,甚至可以把赋值符号省略掉
对于结构体也是如此,可以省略赋值符号,我们这里看起来似乎用处不怎么大,但这种写法是在为后面的内容做准备
上面的是使用的结构体,我们再来看看C++的class是否也一样。 我们从下图中发现没问题,但是不同的写法也是有区别的
像如下这种写法,C++11也是支持的,这样也是挺方便的。
2.initializer_list
再讲解initializer_list之前,我们先来看一下下面这个例子,Date是上面用到的日期类,vector和list是STL库里面的容器。请问,他们后面的{1,2,3}是一样的吗?
这样乍眼一看,好像确实是一样的,实际上他们有本质的区别,d1后面接的{1,2,3}只是d1的三个参数, 他是构造+拷贝构造,被编译器优化为直接构造,并且固定只能写三个参数。
而vector<int> v和 list<int> l 他们两后面接的{1,2,3},是initializer_list,他是库里面的容器。
因为vector和list支持了 initializer_list去进行构造,因此可以这样玩。同时对于参数的个数是不设限制的。
vector和list如此,其他很多容器也是这样,我们随便举个map的例子。
四、右值引用
1.什么是左值
C++中,值可以分为左值和右值,我们之前学的引用都是左值引用,如下
这里并不是说在左边的是左值,而是可以取地址的才是左值,左值引用就是给左值的引用,给左值取别名。如下,虽然第二行 i 在右边,但我们可以给 i 取地址,因此 i 是左值,
2.什么是右值
右值也是一个表示数据的表达式,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。右值引用就是对右值的引用,给右值取别名。
举个例子
3.右值引用写法
右值引用是加&&,比左值还多一个&,如下
左值引用不能给右值取别名,除非加const,缩小权限。
右值引用不能给左值取别名,除非加move()函数将左值转为右值(大家记住就好,具体我们后续了解)
4.右值的分类
内置类型的右值称作纯右值
自定义类型的右值称作将亡值(下面会提到)
5.右值引用的作用
C++11为什么要搞一个右值引用,有什么东西是左值引用解决不了的吗?
我们模拟实现了一个简易版的string,帮助我们打印理解,代码如下
namespace bit
{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);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(const string& s) -- 深拷贝" << endl;/*string tmp(s);swap(tmp);*/if (this != &s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}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)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0; // 不包含最后做标识的\0};bit::string to_string(int x){bit::string ret;while (x){int val = x % 10;x /= 10;ret += ('0' + val);}reverse(ret.begin(), ret.end());return ret;}
}
我们来看下面的例子,我们有一个to_string函数,可以输入整形返回string,我们发现代码竟然发生了两次深拷贝(注意,这里我们是VS2019,使用VS2022编译器优化太厉害,结果会不一样)
具体流程如下,ret(将亡值)出了作用域就会析构,因此如果返回ret的话,他在返回时会拷贝生成一个临时对象,将这个临时对象进行operator= 再赋值给s,这样就会进行两次深拷贝。
这里ret是左值,但是左值引用解决不了问题,因为他出了作用域会析构,内容就不存在了,右值引用也无法解决问题,因为ret为左值,无法进行右值引用。就算你改为move(ret),这样是可以运行,但是ret被析构的事实你无法解决,你内存都还给编译器了,你还想赋值,结果肯定不对。
- 那我们该如何处理呢?这就需要提到移动构造了(转移将亡值的资源,从而避免深拷贝)
如果在拷贝构造的地方重写一个右值引用的构造(被称作移动构造),把将亡值ret的资源转移给那个临时对象,临时对象再去调用operator=进行赋值拷贝,这样是不是可以减少一次深拷贝(移动构造的代价很低)
移动构造代码如下
string(string&& s) {cout << "string(string&& s) -- 移动拷贝" << endl;swap(s); //swap是类里的函数,我们上面的代码中有。 }
我们画图再分析一下,如下是没有写移动构造的情况,先构造一个ret对象,再使用ret对象深拷贝一个临时对象,再使用这个临时对象去operator=深拷贝赋值给s对象。深拷贝的代价比较大,这效率是真的不高。(这里三个_str的地址都不一样,第一次为构造,后两次为深拷贝)
如果我们写了string类的移动拷贝,具体流程就是如下所示了,再ret返回时会进行移动构造而不是拷贝构造,因为ret是右值,重载的右值引用会更加符合。同时在移动构造内只有swap函数,进行资源的交换。这里可以看到下图1和图2string的_str地址都是一样的0x005e0198。因为移动拷贝交换了资源,没有深拷贝提高了效率。
那么我们是不是可以按照这个思路,函数重载一下operator=() 的右值引用版本,进行移动构造,是不是又可以节省一次深拷贝呢?
我们添加如下代码进行operator=的移动拷贝
string& operator=(string&& s)
{cout << "string& operator=(string&& s) -- 移动拷贝" << endl;swap(s);return *this;
}
调试看一看,将亡值ret在析构前将资源给到的临时对象,将亡值临时对象又再析构前将资源给到了s。因此只进行了两次移动拷贝,便完成了操作,三次操作_str的地址都是0x010da9a8。
6.STL容器中的右值引用
由于右值引用的效率比深拷贝高很多,因此C++11在STL容器里,很多地方都用到了右值引用
vector构造函数中的的右值引用
push_back和insert也有右值引用版本
其他容器也是如此,就不多一一举例了
我们直接使用库里面的vector来看一下效果,先reserve(20)是为了防止发生扩容,string进行深拷贝影响我们判断。如下,发生两个移动拷贝,1234转为string类型返回,将亡值ret移动构造了临时对象,将亡值临时对象再移动构造了string类型,最后被添加到 v 后面,好理解。
使用自己模拟的vector试一下。如下是我们之前模拟的vector。将他放到vector.h中,test.cpp文件包一下头文件。
#pragma once
namespace kky
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}vector(){}vector(const vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}vector(int n, const T& val = T()){resize(n, val);}//vector(size_t n, const T& val = T())//{// resize(n,val);//}vector<T>& operator=(vector<T> v){swap(_start, v._start);swap(_finish, v._finish);swap(_endofstorage, v._endofstorage);return *this;}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy对自定义类型是浅拷贝//memcpy(tmp, _start, sz * sizeof(T));//循环赋值,会调用自定义类型的赋值拷贝,就是深拷贝for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}void resize(size_t n, const T& val = T()){if (n <= size()){_finish = _start + n;}else{reserve(n);for (size_t i = size(); i < n; i++){_start[i] = val;}_finish = _start + n;}}void push_back(const T& x){if (size() == capacity()){size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);}*_finish = x;_finish++;}iterator insert(iterator pos, const T& x){assert(pos >= _start);assert(pos <= _finish);if (size() == capacity()){size_t len = pos - _start;//扩容后_start位置可能会发生改变了,因此要记录pos的相对位置,改变pos的值size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator it = pos;while (it < _finish - 1){*it = *(it + 1);++it;}_finish--;return pos;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}//不可修改const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}
测试一下,因为我们自己模拟实现的vector还没有添加右值引用版本的push_back,因此会发生深拷贝,如下
那么我们添加上右值引用的push_back和insert再来试一下
在vector.h添加如下代码(代码就是普通版本拷贝过来的,修改了参数类型为T&& x)
void push_back(T&& x)
{if (size() == capacity()){size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);}*_finish = x;_finish++;
}
iterator insert(iterator pos, T&& x)
{assert(pos >= _start);assert(pos <= _finish);if (size() == capacity()){size_t len = pos - _start;//扩容后_start位置可能会发生改变了,因此要记录pos的相对位置,改变pos的值size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = x;_finish++;return pos;
}
调试一下发现了问题,我们x是右值,进入push_back右值引用里没有问题,但是在下面赋值的地方,竟然会去深拷贝。这是为啥勒?string深拷贝的参数不是左值吗?
- 这是因为被右值引用后,该值的属性会变为左值。C++11为何要这么设计?
大家看下面这个代码,这是我们之前写过的string的移动拷贝,按道理来说,s为右值,是无法被修改的,但这里我们竟然完成了swap交换函数,这下是不是可以讲得通了。不这样设计,右值引用就无法处理,引用来干啥?
那么针对不要进行深拷贝,要去移动拷贝的问题,我们解决起来也很简单,我们只需要给x move一下,让他再变成右值就好了,因为你x本就是右值,就是将亡值,只是右值引用后变成了左值而已,我只是将你变成了本身的状态。
这样就变成了移动拷贝了。
7.万能引用
大家看下面这段代码,第一时间感觉就是我们所学的右值引用,但其实不然。这里使用了模板,如果函数使用了模板参数,并且类型为T&& t。这种类型,那么这就是万能引用,左值和右值都会来调用这里。
使用下面代码进行测试
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;
}
我们发现打印的都是左值引用,这是因为 t 如果是左值,那么左值引用后也是左值,t 如果是右值,右值引用后也是左值,因此打印出来都是左值引用。
我们如何得到想要的效果呢? 这需要用到完美转发,使用forwad<T>(记住就好)
因此vector.h的这两个地方也可以修改为forward<T>(x),同时如果添加上template就可以删除掉拷贝构造了,因为有万能引用跟完美转发了。
总结
我们学习了C++11的两个较为重要的特性,统一的列表初始化,都可以用 {} 去赋值,还学习了右值引用,这里可能会有点啰嗦,因为知识点比较绕,也比较难。
最后附上文本的测试代码,如果是vs2019及以下,大家也可以看看效果(vs2022效果不一样)
vector.h如下
#pragma once
namespace kky
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}vector(){}vector(const vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}vector(int n, const T& val = T()){resize(n, val);}//vector(size_t n, const T& val = T())//{// resize(n,val);//}vector<T>& operator=(vector<T> v){swap(_start, v._start);swap(_finish, v._finish);swap(_endofstorage, v._endofstorage);return *this;}~vector(){delete[] _start;_start = _finish = _endofstorage = nullptr;}size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy对自定义类型是浅拷贝//memcpy(tmp, _start, sz * sizeof(T));//循环赋值,会调用自定义类型的赋值拷贝,就是深拷贝for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}void resize(size_t n, const T& val = T()){if (n <= size()){_finish = _start + n;}else{reserve(n);for (size_t i = size(); i < n; i++){_start[i] = val;}_finish = _start + n;}}template<class T>void push_back(T&& x){if (size() == capacity()){size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);}*_finish = forward<T>(x);_finish++;}template<class T>iterator insert(iterator pos, T&& x){assert(pos >= _start);assert(pos <= _finish);if (size() == capacity()){size_t len = pos - _start;//扩容后_start位置可能会发生改变了,因此要记录pos的相对位置,改变pos的值size_t cp = capacity() == 0 ? 4 : capacity() * 2;reserve(cp);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}*pos = forward<T>(x);_finish++;return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator it = pos;while (it < _finish - 1){*it = *(it + 1);++it;}_finish--;return pos;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}//不可修改const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}
test.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
#include<string>
#include<assert.h>
using namespace std;
#include"vector.h"
namespace bit
{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);}// s1.swap(s2)void swap(string& s){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}// 拷贝构造string(const string& s){cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);int a = 0;}string(string&& s){cout << "string(string&& s) -- 移动拷贝" << endl;swap(s);}// 赋值重载string& operator=(const string& s){cout << "string& operator=(const string& s) -- 深拷贝" << endl;/*string tmp(s);swap(tmp);*/if (this != &s){char* tmp = new char[s._capacity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;_size = s._size;_capacity = s._capacity;}return *this;}string& operator=(string&& s){cout << "string& operator=(string&& s) -- 移动拷贝" << endl;swap(s);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)string& operator+=(char ch){push_back(ch);return *this;}const char* c_str() const{return _str;}private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0; // 不包含最后做标识的\0};bit::string to_string(int x){bit::string ret;while (x){int val = x % 10;x /= 10;ret += ('0' + val);}reverse(ret.begin(), ret.end());return ret;}
}int main()
{kky::vector<bit::string> v;v.reserve(20);bit::string s1("hello world");v.push_back(s1);cout << endl;v.push_back(bit::to_string(1234));
}//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(forward<T>(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;
//}
谢谢大家观看!!!
相关文章:

C++11的列表初始化和右值引用
目录 前言 一、C11的简介 二、C11的小故事。 三、统一的列表初始化 1.列表初始化 2.initializer_list 四、右值引用 1.什么是左值 2.什么是右值 3.右值引用写法 4.右值的分类 5.右值引用的作用 6.STL容器中的右值引用 7.万能引用 总结 前言 C11相较于之C98&…...

千帆起航:探索百度智能云千帆AppBuilder在AI原生应用开发中的革新之路
千帆起航:探索百度千帆AppBuilder在AI原生应用开发中的革新之路 1.揭开帷幕,大模型第二次战役 自从 ChatGPT 横空出世后,一石激起千层浪,人工智能也正在从感知理解走向生成创造,这是一个关键里程碑。生成式大模型完成…...

RevIT™ AAV Enhancer, 提高AAV产量的又一利器!
腺相关病毒 (AAV) 是基因治疗中使用最广泛的传递机制。近年来,基于AAV病毒所开发的基因疗法的研发及临床试验注册数量也呈指数级增长。截止本文撰写之时,美国食品和药物管理局已批准五项AAV疗法,也是全球市场上最为昂贵的药物,其中…...

Kubectl 部署有状态应用(下)
接上文 《Kubectl 部署有状态应用(上)》创建完StatefulSet后,本文继续介绍StatefulSet 扩展、更新、删除等内容。 StatefulSet 中的 Pod 验证序数索引和稳定的网络身份 StatefulSet 中的 Pod 具有唯一的序数索引和稳定的网络身份。 查看 …...

Jmeter 性能 —— 监控服务器!
Jmeter监控Linux需要三个文件 JMeterPlugins-Extras.jar (包:JMeterPlugins-Extras-1.4.0.zip)JMeterPlugins-Standard.jar (包:JMeterPlugins-Standard-1.4.0.zip)ServerAgent-2.2.3.zip 1、Jemter 安装插件 在插件管理中心的搜索Servers Performan…...

离散型制造企业为什么要注重MES管理系统的实施
离散型制造企业经常面临三个核心问题:生产什么、生产多少以及如何生产。尽管许多企业都实施了ERP系统,但仍然绕不开MES管理系统的话题。本文将从三个方面详细解释为什么离散型企业需要实施MES管理系统。 一、生产线经常出现的问题 在离散型企业中&#…...

Linux系统中跟TCP相关的内核参数
1. TCP保活机制 参考 《Nginx(三) 配置文件详解 - 基础模块》3.18章节 net.ipv4.tcp_keepalive_intvl:设置两次相邻探活检测的间隔时间。默认是75秒,单位是秒。net.ipv4.tcp_keepalive_probes:设置探活最多检测次数。默认是9次,单…...

代理模式(Proxy)
代理模式(Proxy Pattern)是一种结构型设计模式,用于为另一个对象提供一个代替品或占位符以控制对这个对象的访问。这个模式主要用于延迟处理操作或者在进行实际操作前后进行其它处理。 代理模式的实现通常涉及以下角色: 抽象主题(Subject):定义了代理和真实对象的共用接…...

在MacOS上Qt配置OpenCV并进行测试
目录 一.Qt环境准备 二.在Qt项目中加载Opencv库并编写代码测试 1.使用Opencv加载图片 (1)在Qt中创建一个新项目 (2)在.pro文件中链接OpenCV库 (3)添加新资源文件 (4)在mainw…...

java数据结构与算法刷题-----LeetCode167:两数之和 II - 输入有序数组
java数据结构与算法刷题目录(剑指Offer、LeetCode、ACM)-----主目录-----持续更新(进不去说明我没写完):https://blog.csdn.net/grd_java/article/details/123063846 思路 题目要求我们找到两个数相加的和,等于target指定的值。而…...

Linux:jumpserver V3的安装与升级(在线离线)(2)
官方文档写的非常详细,我这篇文章时间长了,会随着官方版本更新而落后 JumpServer - 开源堡垒机 - 官网https://www.jumpserver.org/安装和升级在官网也有详细的信息,我写本章是为了记录一下实验 我的系统是centos7.9 在线安装 在确定我们可…...

【GoLang】Go语言几种标准库介绍(一)
你见过哪些令你膛目结舌的代码技巧? 文章目录 你见过哪些令你膛目结舌的代码技巧?前言几种库bufio(带缓冲的 I/O 操作)特性示例 bytes (实现字节操作)特性示例 总结专栏集锦写在最后 前言 随着计算机科学的迅猛发展,编…...

短剧分销系统:月入百w的新模式
随着我国短剧的高速发展,越来越多的人进入到了短剧影视行业。本文旨在介绍短剧市场的发展前景以及短剧分销系统的设计和开发。 一、短剧发展背景 短剧具有时长短、剧情紧凑、节奏快、剧情新颖等特点,满足了国内观众的碎片化时间,在当下短视频…...

鞋服用户运营策略如何实现有效闭环?
实现长期价值和业务闭环是企业经营的关键。对于鞋服行业来说,如何基于客户旅程编排(Customer Journey Orchestration,简称 CJO)实现用户运营策略的有效闭环,提升长期价值呢? 本文围绕该主题,从鞋…...

简单工厂、工厂方法、抽象工厂和策略模式
摘要 本文简单介绍软件开发过程中面临的痛点和几个总体原则。详细介绍了简单工厂、工厂方法、抽象工厂和策略模式的实现,以及各种模式之间的相似、区别。 背景 开发面临哪些问题(痛点)? 相信做过大型软件开发的tx都遇到过以下类似…...

junit mocktio request打桩
Controller下request组装参数 HttpServletRequest request new MockHttpServletRequest(); ((MockHttpServletRequest) request).addHeader("router","login"); ((MockHttpServletRequest) request).addParameter("test","wwww"); …...

第十四节TypeScript 联合类型
1、简介 联合类型可以通过管道(|)将变量设置多种类型,赋值时可以根据设置的类型来赋值。 注意:只能赋值指定的类型,如果赋值其它类型就会报错的。 2、创建联合类型的语法格式: Type1|Type2|Type3 实例&a…...

[x86汇编语言]从实模式到保护模式第二版
下载汇编器:https://www.nasm.us/pub/nasm/releasebuilds/2.16.02rc6/win64/ mov ax, 0x3f add bx,ax add cx,ax 编译: C:\Users\HP>cd D:\BaiduNetdiskDownload\01b站\lizhong\myasm C:\Users\HP>D: D:\BaiduNetdiskDownload\01b站\lizhong…...

基本的逻辑门
前言 本篇文章介绍基本的逻辑门,然后给出C语言描述 逻辑门是在集成电路上的基本组件。简单的逻辑门可由晶体管组成。这些晶体管的组合可以使代表两种信号的高低电平在通过它们之后产生高电平或者低电平的信号。高、低电平可以分别代表逻辑上的“真”与“假”或二进…...

云原生系列3-Kubernetes
1、Kubernetes概述 k8s缩写是因为k和s之间有八个字符。k8s是基于容器技术的分布式架构方案。官网:https://kubernetes.io/zh-cn/ Google在 2014年开源了Kubernetes项目,Kubernetes是一个用于自动化部署、扩展和管理容器化应用程序的开源系统。同样类似的…...

R-列表、矩阵、数组转化为向量
目录 一、c()函数 二、unlist()函数 一、c()函数 c():对应的英文是combine. 当你使用c()函数时,它会将输入的对象连接成一个向量。因此,无论输入是矩阵、数组还是列表,c()函数都会将它们连接成一个简单的向量。因此ÿ…...

算法通关村-番外篇排序算法
大家好我是苏麟 , 今天带来番外篇 . 冒泡排序 BubbleSort 最基本的排序算法,最常用的排序算法 . 我们以关键字序列{26,53,48,11,13,48,32,15}看一下排序过程: 动画演示 : 代码如下 : (基础版) class Solution {public int[] sortArray(int[] nums) {for(int i …...

三种方式简单搭建http本地文件服务
有时候想写一个简单的html文件,然后加上一些image、js、css文件用于测试。希望有一个简单的http服务,总结了如下三种方式,欢迎讨论更多高效的方式。 (一)使用Web Server for Chrome浏览器扩展 之前写过一篇博文&#x…...

设计模式--适配器模式
实验8:适配器模式 本次实验属于模仿型实验,通过本次实验学生将掌握以下内容: 1、理解适配器模式的动机,掌握该模式的结构; 2、能够利用适配器模式解决实际问题。 [实验任务]:双向适配器 实现一个双向…...

Node.js教程-express框架
概述 Express是基于Node.js平台(建立在Node.js内置的http模块上),快速、开放、极简的Web开发框架。 中文官网 http://www.expressjs.com.cn/。 Github地址:https://github.com/orgs/expressjs。 Express核心特性: 可设置中间件来响应 HTTP…...

location.origin兼容
if (!window.location.origin) {window.location.origin window.location.protocol "//" window.location.hostname (window.location.port ? : window.location.port: );}...

spring boot集成mybatis和springsecurity实现权限控制功能
上一篇已经实现了登录认证功能,这一篇继续实现权限控制功能,文中代码只贴出来和上一篇不一样的修改的地方,完整代码可结合上一篇一起整理spring boot集成mybatis和springsecurity实现登录认证功能-CSDN博客 数据库建表 权限控制的意思就是根…...

按键修饰符
在键盘监听事件时,我们经常需要判断详细的按键,此时,可以为键盘相关的事件添加按键修饰符,例如: 键盘修饰符案例:...

新版IDEA中Git的使用(一)
说明:本文介绍如何在新版IDEA中使用Git 创建项目 首先,在GitLab里面创建一个项目(git_demo),克隆到桌面上。 然后在IDEA中创建一个项目,项目路径放在这个Git文件夹里面。 Git界面 当前分支&Commit …...

【性能测试】真实企业,性能测试流程总结分析(一)
目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 性能测试什么时候…...