【C++进阶之路】C++11(上)

文章目录

  • 一、列表初始化
    • 1.{}
    • 2.initializer_list
  • 二、声明
    • 1.auto
    • 2.deltype
  • 三、右值与左值
    • 1.基本概念
    • 2.应用场景
      • 1.左值引用
      • 2.右值引用
      • 3.完美转发
      • 4.万能引用
  • 四、新增默认成员函数
  • 五、lambda表达式
    • 1.基本语法
      • 1.1捕捉列表
      • 1.2参数列表
      • 1.3返回类型
      • 1.4函数体
    • 2.底层原理
  • 总结

一、列表初始化

1.{}

  • 内置类型
	int a1{ 1 };int a2 = { 2 };
  • 自定义类型
	class Point{public:Point(int a,int b):_a(a),_b(b){}private:int _a;int _b;};int main(){//数组int arr1[]{ 1,2,3 };//列表初始化int arr2[] = { 1,2 };//数组初始化。//自定义类型初始化Point p1{ 1,2 };//这里是显示调用构造函数Point p2 = { 1,2 };//本质上是C++11支持多参数的隐式类型转换,编译器优化为直接构造。//验证:在构造函数前面+explicit 语法会报错。const Point& p4 = { 1,2 };//多参数的隐式类型转换,生成一个匿名对象其具有常性,不可被修改。int* ptr1 = new int[] {1, 2, 3};Point* ptr2 = new Point[]{{1,2},{1,3}};//外面的一层为数组,里面的为列表初始化。return 0;}

总结:

  • 一切即可用{}进行初始化。
  • {}支持多参数的隐式类型转换,也用于单参数的隐式类型转换。
  • 对于自定义类型初始化不加 = 直接跟 {}——显示调用构造函数。
  • 对于加 = 再跟{}初始化——隐式类型转换+编译器的优化 - > 调用构造函数(构造函数前加explicit验证)。

2.initializer_list

  • initializer_list是编译器特殊支持的内置容器,不可单独实现,是一个常量数组。
    在这里插入图片描述
    说明:这是库里initializer_list的所有接口。
	int main(){const char* str = "abcd";//常量区字符串。返回的是首字母的地址。int arr[] = {1,2,3,4};//直接进行赋值。initializer_list<int> lt;//此处是默认构造函数进行初始化initializer_list<int> lt = { 1,2,3,4 };//常量区数组,此处存于栈区。//此处是编译器直接进行识别进行初始化,可以理解是直接内存赋值,//与arr数组的赋值雷同。return 0;}
  • 常见用途:作为构造函数的参数进行初始化/赋值
    在这里插入图片描述

  • 注意

	vector<int> v = { 1,2,3,4 };//隐式类型转换 + 编译器优化,调用initializer_list的构造函数进行初始化。Point p = { 1,2 };//隐式类型转换+编译器优化,调用相应的构造函数进行初始化。
  • 接口实现
	vector(initializer_list<T>& lt){reserve(lt.size());for(auto e : lt){push_back(e);}//或者直接调用相应的迭代器区间进行初始化,不过得用现代写法再进行交换。//注意:构造函数是不能够显示进行调用的!//vector tmp(lt.begin(), lt.end());//swap(tmp);}

其它的接口类似。

二、声明

1.auto

详见:初始C++语法

举例:

int main()
{auto p1 = malloc;auto p2 = Point(1, 2);auto p3 = { 1,2,3 };auto p4 = "123";auto p5 = 1;auto str = string();cout << typeid(p1).name() << endl;cout << typeid(p2).name() << endl;cout << typeid(p3).name() << endl;cout << typeid(p4).name() << endl;cout << typeid(p5).name() << endl;cout << typeid(str).name() << endl;cout << endl;//typeid().name的返回值的类型为char const* ,即指向的内容不可被修改。cout << typeid(typeid(p1).name()).name() << endl;return 0;
}

补充:auto是在编译期间进行推导得出类型的,且右边必须有可推导的值,因此不可对类型进行声明。

2.deltype

  • 可得出类型,且可以当做类型名进行使用。
template<class T>
class Func
{T _a;
};
int main()
{auto p1 = malloc;decltype(p1) p2;Func<decltype(p2)> p3;return 0;}
  • 总结
  1. 推导出的类型可以进行声明。
  2. 推导出的类型可以当做函数参数。

用途:一些迭代器类型的声明,以及复杂指针的声明。

三、右值与左值

1.基本概念

  • 右值:不能取地址,一般把内置类型的右值叫做纯右值,自定义类型的右值叫将亡值。
  • 左值:能取地址。
  • 左值引用:对左值起别名。
  • 右值引用:对右值起别名。
  • 引用:不管左值引用还是右值引用,都是对值起别名。

  • 常见右值
int add(int x, int y)
{return x + y;
}
int main()
{int a = 0;int b = 0;//常见的右值//表达式a + b;//函数的返回值add(a, b);//常量10;//验证:不能取地址。//cout << &10 << &add(a, b) << &(a + b) << endl;return 0;
}

特殊的左值:

int main()
{//特殊的左值。"abc";//返回的是首字符a的地址。const char* p = "abc";p[1];//这也是左值。return 0;
}

  • 左值引用
int main()
{int a = 0;int b = 0;//左值引用直接对左值取别名int& ref1 = a;int& ref2 = b;//const 左值引用可以引用右值const int& ref3 = a + b;const int& ref4 = 10;return 0;
}

拓展:

	const int& ref3 = 10;int* ptr = (int*)&ref3;*ptr = 100;cout << &ref3 << endl;cout << ref3 << endl;//const左值引用引用右值,开空间存储右值,对const左值引用可以取地址,因此const左值引用为左值。

  • 右值引用
int main()
{int a = 0, b = 0;int&& ref5 = 10;int&& ref6 =  a + b;return 0;
}
  • 拓展
int main()
{int a = 0;int&& ref = move(a);//cout << &move(a) << endl;//右值引用可以引用move之后的左值,move之后的结果为右值。int&& ref1 = 10;cout << &ref1 << endl;//右值引用能取地址,说明右值引用为左值,对10开空间进行拷贝。ref1++;//右值引用可以进行修改,更说明其为左值。int&& ref2 = forward<T>(ref1);//叫做完美转发,即保持右值属性,此处了解即可。int&& ref3 = move(ref2);//将左值ref2转换为右值。//说明:右值引用也可作为右值,需要进行处理。return 0;
}

2.应用场景

1.左值引用

  1. 作为函数参数
  2. 作为返回值,前提是引用的值在出作用域不被销毁。
  • 起到的作用都是减少拷贝。

缺陷:当返回一个局部的自定义的很大的对象时,只能用传值返回,不能用传引用返回。

说明:写一份string方便查看细节:

namespace MY_STL
{class string{public:typedef char* iterator;iterator begin(){return _str;}iterator end(){return _str + _size;}string(const char* str = ""):_size(strlen(str)), _capacity(_size){_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):_str(nullptr){cout << "string(const string& s) -- 深拷贝" << endl;_size = s._size;_capacity = s._capacity;_str = new char[_capacity + 1];strcpy(_str, s._str);}// 赋值重载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)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; // 不包含最后做标识的\0};
}
  • 举例:
MY_STL::string func()
{MY_STL::string str("xxxxxxxxxxxxxxxxxx");return str;
}int main()
{//1.连续的拷贝构造进行优化。MY_STL::string str = func();//2.不连续的拷贝构造,进行逐步拷贝。MY_STL::string str1;str1 = func();return 0;
}

1.图解
在这里插入图片描述

说明:编译器优化的结果是——在return之前就调用拷贝构造进行构造,因为return后就调用析构函数对空间进行销毁了。

2.图解
在这里插入图片描述

运行结果图解:

在这里插入图片描述

2.右值引用

 前面我们举了左值引用的缺陷,那右值引用是来弥补这一个缺陷的,如何弥补呢?接着继续分析。

第一个问题:

void func(const int& a)
{cout << "func(const int& a)" << endl;
}
void func(int && a)
{cout << "func(int && a)" << endl;
}
int main()
{int a = 0;int b = 0;func(a);func(a + b);return 0;
}

运行结果如何?构成重载么?如果构成,那是否存在歧义?

  • 运行结果
    在这里插入图片描述
    结果很显然构成重载,且不存在歧义,函数调用会走最匹配的。

接着用上面的例子:

MY_STL::string func()
{MY_STL::string str("xxxxxxxxxxxxxxxxxx");return str;
}int main()
{MY_STL::string str1;str1 = func();return 0;
}

 根据前面的思路分析,返回str时发生了一次拷贝构造,生成了匿名对象(右值),且其声明周期只在这一行,那么我们是否可以利用这一特性写出个右值引用版本的赋值呢?那关键就在于这是右值且声明周期只在当前一行,因此我们可以直接换资源,我们顺走匿名对象的资源,之后让匿名对象带走不用的资源(析构函数)

根据这样我们写出来的赋值重载就是这样的:

void swap(string& s)
{::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);
}
string& operator=(string&& s)
{cout << "string& operator=(string&& s) -- 移动赋值" << endl;swap(s);return *this;
}
  • 补充:右值引用可以被修改,且默认识别为左值。

我们再来运行一下:

在这里插入图片描述

 对比之前,这里就优化了一次深拷贝,在一定程度上提升了效率。


继续使用上面的例子再进行分析:

MY_STL::string func()
{MY_STL::string str("xxxxxxxxxxxxxxxxxx");return str;
}int main()
{//1.连续的拷贝构造进行优化。MY_STL::string str = func();return 0;
}

 按理来说这里本该为拷贝构造生成匿名对象,再用匿名对象拷贝构造对象,但编译器看不惯连续的拷贝构造,因此直接优化为了一次拷贝构造,这里如何用右值进行优化呢?编译器直接将返回的str特殊处理成右值,然后用识别之后的右值,进行拷贝构造,那如何写拷贝构造呢?其实跟赋值一样。

string(string&& s):_str(nullptr)
{cout << "string(string&& s)——移动拷贝" << endl;swap(s);
}

运行结果:
在这里插入图片描述
看结果,确实是编译器对返回值进行了优化,那为啥要进行这样的优化呢?第一点博主认为这样代码具有可移植性,第二点是不用再对之前的代码进行修改即在返回值处修改成move(str)。

此时我们再看之前的那个例子:

MY_STL::string func()
{MY_STL::string str("xxxxxxxxxxxxxxxxxx");return str;
}int main()
{//1.连续的拷贝构造进行优化。MY_STL::string str = func();return 0;
}

再看运行结果:

在这里插入图片描述
这里编译器也是做了优化的,对返回值直接识别成右值,直接将两次拷贝构造,优化为两次移动构造,这样处理对一些很大的自定义的值返回,带来了效率的极大的提升!并且写起来也变的更加轻松。


除此之外为了更好更深刻的理解问题,博主举一个例子:

int main()
{const string& str = string("xxxxx");string("xxxxx").push_back('a');return 0;
}

这会引出来几个问题:

  1. 匿名对象具有常属性不可以被修改,但是这里却可以调用接口被修改。
  2. 匿名对象左值引用只能用const左值引用,之后就不可调用接口修改,因为是this指针被const修饰,这一点很容易理解。
  3. 匿名对象右值引用可以被修改,且修改的是匿名对象本身,而对内置类型则是修改其拷贝。

 可以看出匿名对象确实是右值或者说是常量(const左值引用),但也确实可以被修改,其原因就在于这是C++委员会开的绿灯,其目的就在于通过右值引用修改其内部成员而完成资源的转换,进而实现移动赋值(语义)和移动拷贝(语义),而对内置类型的右值进行引用则是开空间存储(消耗不大),因此我们可以把右值引用的最终目的就是为了减少资源的拷贝进行提升效率。

总结一下关键:

  1. 对于内置类型的右值可以认为是常量绝对不可以被修改,而自定义类型的右值也认为是常量,但为了达到目的,开了绿灯因此可以被修改。
  2. 右值引用对于内置类型开空间拷贝,对自定义类型直接取别名,且右值引用之后的别名可以进行修改,且被识别为左值(方便修改),进而方便转资源,提升效率。

 那右值引用是否具有右值属性呢?答案是肯定的。在有些情况下,是需要保持右值属性的。

比如:当我们在vector中push_back一个string。
在这里插入图片描述

int main()
{MY_STL::vector < MY_STL::string> v;v.push_back("xxxxx");//尾插一个匿名对象的string。return 0;
}

因此push_back可优化成这样:

在这里插入图片描述
 但是又涉及一个问题,虽然这里是右值引用,但这里会被识别为左值,进而调用赋值重载跟上面的一样,这要怎么办呢?也就是如何保持其右值的属性呢?

3.完美转发

 完美转发就是来解决上面的问题,右值引用保持其右值的属性,左值引用保持其左值的属性。
 那么最终的push_back就是:

void push_back(value_type&& val)
{if (_finish == _end_of_storage){size_t new_capacity = size() == 0 ? 4 : \capacity() * 2;//扩容reserve(new_capacity);}*(_finish++) = forward<value_type>(val);//这样就叫做完美转发。
}
  • 这样就能在插入数据时进一步优化效率。

4.万能引用

 最后再简单的提及一个比较省力的小语法。

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(move(a)); // 右值const int b = 8;PerfectForward(b); // const 左值PerfectForward(move(b)); // const 右值return 0;
}

运行结果:
在这里插入图片描述
图解:
在这里插入图片描述

四、新增默认成员函数

 C++11新增了两个默认成员函数,不过需要具备以下条件才会自动生成。

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

例:

class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}
private:MY_STL::string _name;int _age;
};
int main()
{Person s2;Person s3("xxxxx",10);s3 = move(s2);return 0;
}

 分析为什么要这样设计?关键在于默认生成的移动构造函数对内置类型是只拷贝字节的。也就是说我们在类内部定义一个指针,用个指针指向堆空间的资源,默认生成只完成浅拷贝,而不完成资源的交换!并且因为默认生成的析构函数不会对内置类型处理,因此还会造成内存泄漏 , 这就要求我们自己实现析构函数,但是写了析构函数就不会生成默认的移动构造了,这就又反过来要求我们自己写移动构造交换资源。
 这样编译器选择生成的目的在于只要你写了实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个就会默认认为有需要深拷贝的内置成员,因此不会生成只会对内置类型完成按字节拷贝的默认移动构造。

  • 对于移动赋值重载同理。

五、lambda表达式

 lambda其实就是一个对象,有类似函数,可以在语句里面定义即使用。

在之前我们还学过函数指针和仿函数与之相同。

1.基本语法

基本语法:

[capture-list] (parameters) mutable -> return-type { statement}
//捕捉列表          参数      控制属性     返回类型        实现语句

先举个例子看看如何使用:

struct Goods
{string _name; // 名字double _price; // 价格int _evaluate; // 评价Goods(const char* str, double price, int evaluate):_name(str), _price(price), _evaluate(evaluate){}
};
int main()
{vector<Goods> v = { { "苹果", 2.1, 5 }, { "香蕉", 3, 4 }, { "橙子", 2.2, 3 }, { "菠萝", 1.5, 4 } };auto CompareNameLess = [](const Goods& x1, const Goods& x2)->bool { return x1._name < x2._name; };auto CompareNameGreater = [](const Goods& x1, const Goods& x2)->bool{ return x1._name > x2._name; };sort(v.begin(), v.end(), CompareNameLess);sort(v.begin(), v.end(), CompareNameGreater);//也可以直接这样写:sort(v.begin(), v.end(), [](const Goods& x1, const Goods& x2)->bool { return x1._name < x2._name; });sort(v.begin(), v.end(), [](const Goods& x1, const Goods& x2)->bool { return x1._name > x2._name; });//箭头以及后面的类型可省略,编译器会自动进行推导。sort(v.begin(), v.end(), [](const Goods& x1, const Goods& x2){ return x1._name > x2._name; });return 0;
}

 这样使用其实有一定的好处,就是避免了有人使用仿函数或者函数指针时,定义的类或者函数名字不规范而导致代码的可读性极差,还要回去找定义才能进行理解, 花费的时间成本很大。

再来谈谈其具体的几个部分:

1.1捕捉列表

 捕捉列表其实是用所在局部域的变量,因为其实lambda就是一个类的匿名对象,其定义的内容在底层是一个函数,又因为函数与函数之间的域是独立的,不可互相使用变量只能通过传参的形式或者使用全局变量的方式间接使用,这里的捕捉列表就是为了间接使用不同域之间的变量而诞生的。

  • 捕捉值
  1. 捕捉指定值。
int main()
{int x = 1, y = 2;auto tmp = [x, y]() { //x = 0;并且此处是const的左值,不可被修改。return x + y; };return 0;
}

 这里的x与y进行值捕捉是加const的,因此不可被修改,如果非要修改呢?

 很简单:在()后加 mutable。

这样就可以被修改了:

int main()
{int x = 1, y = 2;auto tmp = [x, y]() mutable{ x = 0;return x + y; };return 0;
}

说明: mutable:默认情况下,lambda函数总是一个const函数,mutable可以取消其常量性。使用该修饰符时,参数列表不可省略(即使参数为空)。

class Person {
public:void modify()const{//const函数//比如int float等类型,其值不可修改。//指针的值不可被修改,内容是可以被修改的。//引用底层是指针,其指向的内容是可以被修改的。//static静态对象不受this指针的约束也可以被修改。//age++;//name = "aaaa";nums++;*name;ref = 0;_id = 0;}private:int age;int& ref = age;char* name;mutable int nums;static int _id;
};
int Person::_id = 0;
  1. 捕捉所有值(如果有this则this也算进去)
int main()
{int x = 1, y = 2;auto tmp = [=]() mutable{ x = 0;return x + y; };return 0;
}

  • 捕捉引用
  1. 捕捉指定引用
int main()
{int x = 1,y = 2;auto tmp = [&x,&y](){ return x + y; };return 0;
}
  1. 捕获所有引用(如果有this包含this)
int main()
{int x = 1,y = 2;auto tmp = [&](){ return x + y; };return 0;
}

注意

  1. 捕捉列表不允许变量重复传递,否则就会导致编译错误,比如x以值的形式捕捉过一回就不能用引用再进行捕获。
  2. 语法上捕捉列表可由多个捕捉项组成,并以逗号分割。比如:[=, &a, &b]:以引用传递的方式捕捉变量a和b。
  3. 在全局定义lambda表达式,其捕捉列表必须为空。
  4. lambda只能捕捉其所在局部作用域的变量。
  5. lambda之间不能能进行赋值,因为类型不同。

1.2参数列表

  • 与普通的函数列表一样。

1.3返回类型

  • 通常可通过实现体进行推导,所以一般情况下可以省略。

1.4函数体

  • 写函数的实现功能。

2.底层原理

  1. 类型
int main()
{auto tmp = []() {};cout << typeid(tmp).name() << endl;return 0;
}

运行结果:
在这里插入图片描述

可以看出其是一个类,至于这么长,博主猜测是采用了哈希函数的那套方法进行映射出了一段关键码,由此产生不同的类,这也间接说明了不同lambda表达式的类型各不相同。

  1. 反汇编
struct tmp1
{bool operator()(){return 0;}
};
int main()
{auto tmp = []()->bool { return 0; };tmp();tmp1()();return 0;
}

反汇编截图:
在这里插入图片描述

  • 因此:底层是仿函数。

总结

 今天的分享到此就结束了,下篇文章再见咯,如果有所帮助,不妨点个赞鼓励一下吧!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一个高精度24位ADC芯片ADS1222的使用方法及参考电路程序成都控制器定制

前一段时间&#xff0c;在做单片机、PLC、电路板、控制器/箱、仪器仪表、机电设备或系统、自动化、工控、传感、数据采集、自控系统、控制系统&#xff0c;物联网&#xff0c;电子产品&#xff0c;软件、APP开发设计定制定做开发项目时&#xff0c;有要求用到24位的高精度ADC&a…

唤醒手腕 Matlab 游戏编程常用技术知识点详细教程(更新中)

Figure 窗口初始化 figure 使用默认属性值创建一个新的图窗窗口。生成的图窗为当前图窗。f figure(___) 返回 Figure 对象。可使用 f 在创建图窗后查询或修改其属性。figure(f) 将 f 指定的图窗作为当前图窗&#xff0c;并将其显示在其他所有图窗的上面。 figure(n) 查找 Nu…

数量关系 --- 方程

目录 一、代入排除法 例题 练习 二、数字特性 例题 练习 整除特性 例题 倍数特性 普通倍数 因子倍数 比例倍数 例题 练习 三、方程法 例题 练习 四、 不定方程&#xff08;组&#xff09; 例题 练习 一、代入排除法 例题 素数&#xff1a…

亲测可用国产GPT人工智能

分享一些靠谱、可用、可以白嫖的GPT大模型。配合大模型&#xff0c;工作效率都会极大提升。 清华大学ChatGLM 官网&#xff1a; 智谱清言中国版对话语言模型&#xff0c;与GLM大模型进行对话。https://chatglm.cn/开源的、支持中英双语的1300亿参数的对话语言模型&#xff0…

数据结构与算法基础(青岛大学-王卓)(8)

哎呀呀&#xff0c;sorry艾瑞波地&#xff0c;这次真的断更一个月了&#xff0c;又发生了很多很多事情&#xff0c;秋风开始瑟瑟了&#xff0c;老父亲身体查出肿瘤了&#xff0c;有病请及时就医&#xff0c;愿每一个人都有一个健康的身体&#xff0c;God bless U and FAMILY. 直…

c++三大概念要分清--重载,隐藏(重定义),覆盖(重写)

目 录 一、重载 **&#xff08;1&#xff09;概念&#xff1a;**在同一个作用域内&#xff1b;函数名相同&#xff0c;参数列表不同&#xff08;参数个数不同&#xff0c;或者参数类型不同&#xff0c;或者参数个数和参数类型都不同&#xff09;&#xff0c;返回值类型可相同也…

手机号码格式校验:@PhoneQuery(作为查询参数)(自定义参数校验注解)

目标 自定义一个用于校验&#xff08;作为查询参数的&#xff09;手机号码格式的注解PhoneQuery&#xff0c;能够和现有的 Validation 兼容&#xff0c;使用方式和其他校验注解保持一致。 校验逻辑 可以为 null 或 空字符串&#xff1b;不能包含空格&#xff1b;必须为数字序…

JUC中的设计模式

文章目录 1. 终止模式之两阶段终止模式 1. 终止模式之两阶段终止模式 需求&#xff1a;用一个线程每两秒检测***状态&#xff0c;当不想检测时&#xff0c;用另一个线程将其停止 在一个线程 T1 中如何“优雅”终止线程 T2&#xff1f;这里的【优雅】指的是给 T2 一个料理后事…

十一,从摄像机打印HDR环境贴图

越来越接近真相了。我们很自然地想到&#xff0c;如果把漫游器放在中心打印&#xff0c;是不是就可以打印整个等距柱状投影图了呢&#xff1f;是的&#xff0c;但是&#xff0c;只是要注意的是&#xff0c;立方体贴图的内部和外部尽管一样&#xff0c;但是还是稍微有点模糊&…

Llama2-Chinese项目:4-量化模型

一.量化模型调用方式   下面是一个调用FlagAlpha/Llama2-Chinese-13b-Chat[1]的4bit压缩版本FlagAlpha/Llama2-Chinese-13b-Chat-4bit[2]的例子&#xff1a; from transformers import AutoTokenizer from auto_gptq import AutoGPTQForCausalLM model AutoGPTQForCausalLM…

实用调试技巧

引言&#xff1a;一个完美的代码离不开程序员的调试&#xff0c;所谓三分编写七分调试&#xff0c;今天我们给大家介绍几种实用的调试技巧。 1️⃣Bug的由来&#xff1a; 原意是指&#xff0c;小虫子&#xff0c;昆虫等&#xff0c;而人们也通常将电脑程序中的一些隐藏的缺陷或…

【GESP考级C++】1级样题 闰年统计

GSEP 1级样题 闰年统计 题目描述 小明刚刚学习了如何判断平年和闰年&#xff0c;他想知道两个年份之间&#xff08;包含起始年份和终止年份&#xff09;有几个闰年。你能帮帮他吗&#xff1f; 输入格式 输入一行&#xff0c;包含两个整数&#xff0c;分别表示起始年份和终止…

ChatGPT多模态升级,支持图片和语音,体验如何?

一、前言 9 月 25 日&#xff0c;ChatGPT 多模态增加了新的语音功能和图像功能。这些功能提供了一种新的、更直观的界面&#xff0c;允许我们与 ChatGPT 进行语音对话或展示我们正在谈论的内容。 ChatGPT 现在可以看、听、和说话了&#xff0c;而不单单是一个文本驱动的工具了。…

linux系统与应用

Windows中的硬盘和盘符的关系&#xff1b; 硬盘通常为一块到两块&#xff1b;数量与盘符没有直接关系&#xff1b;一块硬盘可以分为多个盘符&#xff0c;如c,d,e,f,g等&#xff1b;当然理论上也可以一块硬盘只有一个盘符&#xff1b;学习linux时&#xff0c;最好使用固态硬盘&a…

Leetcode 450. 删除二叉搜索树中的节点

文章目录 题目代码&#xff08;10.2 首刷看解析&#xff09; 题目 Leetcode 450. 删除二叉搜索树中的节点 代码&#xff08;10.2 首刷看解析&#xff09; class Solution { public:TreeNode* deleteNode(TreeNode* root, int key) {if(!root)return root;if(root->val <…

基于Java的厨艺交流平台设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

linux Mysql 8.0.16 安装搭建

文章目录 Mysql 搭建一、安装包下载二、创建用户组用户和修改权限三、配置my.cnf Mysql 搭建 一、安装包下载 mysql 下载地址&#xff1a;https://downloads.mysql.com/archives/community/ 这里有所有的mysql的版本&#xff0c;下载自己需要的版本&#xff0c;我们这里下载 …

leetCode 122.买卖股票的最佳时机 II 贪心算法

122. 买卖股票的最佳时机 II - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 prices &#xff0c;其中 prices[i] 表示某支股票第 i 天的价格。 在每一天&#xff0c;你可以决定是否购买和/或出售股票。你在任何时候 最多 只能持有 一股 股票。你也可以先购买&…

奥斯卡·王尔德

奥斯卡王尔德 奥斯卡王尔德&#xff08;Oscar Wilde&#xff0c;1854年10月16日—1900年11月30日&#xff09;&#xff0c;出生于爱尔兰都柏林&#xff0c;19世纪英国&#xff08;准确来讲是爱尔兰&#xff0c;但是当时由英国统治&#xff09;最伟大的作家与艺术家之一&#xf…

搭建全连接网络进行分类(糖尿病为例)

拿来练手&#xff0c;大神请绕道。 1.网上的代码大多都写在一个函数里&#xff0c;但是其实很多好论文都是把网络&#xff0c;数据训练等分开写的。 2.分开写就是有一个需要注意的事情&#xff0c;就是要import 要用到的文件中的模型或者变量等。 3.全连接的回归也写了&#…