【C++】C++11【上】列表初始化|声明|新容器|右值引用|完美转发|新的类功能

目录

1、 C++11简介

2、 统一的列表初始化

2.1 {}初始化

2.2 std::initializer_list

 3、声明

 3.1 auto和范围for

3.1decltype

3.3 nullptr

 4、新容器

5、 右值引用

5.1左值引用和右值引用

5.2 左值引用与右值引用比较 

5.3 左值和右值引用使用场景及意义

 6、完美转发

7、新的类功能

7.1默认成员函数

7.2强制生成默认函数的关键字default:

7.2禁止生成默认函数的关键字delete:


 C++11特性我分为【上】【下】两部分来写,本文介绍【上】

1、 C++11简介

C++0x C++11 C++ 标准 10 年磨一剑,第二个真正意义上的标准珊珊来迟。相比于 C++98/03 C++11 则带来了数量可观的变化,其中包含了约 140 个新特性,以及对 C++03 标准中 600 个缺陷的修正,这使得 C++11 更像是从 C++98/03 中孕育出的一种新语言。相比较而言, C++11 能更好地用于系统开发和库开发、语法更加泛华和简单化、更加稳定和安全,不仅功能更 强大,而且能提升程序员的开发效率,公司实际项目开发中也用得比较多,所以我们要作为一个 重点去学习 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

2、 统一的列表初始化

2.1 {}初始化

C++98 中,标准允许使用花括号 {} 对数组或者结构体元素进行统一的列表初始值设定。比如:
struct Point
{int _x;int _y;
};int main()
{int array1[] = { 1,2,3,4,5 };int array2[5] = { 0 };Point p = { 1, 2 };return 0;
}
C++11 扩大了用大括号括起的列表 ( 初始化列表 ) 的使用范围,使其可用于所有的内置类型和用户自
定义的类型, 使用初始化列表时,可添加等号 (=) ,也可不添加
class Point
{
public:Point(int x = 0, int y = 0) : _x(x), _y(y){}private:int _x;int _y;
};int main()
{int x = 1;int y{ 2 };//c++11,没必要学,没意义//C++11才支持的花括号列表初始化(容器都可以用{}初始化,数组也适用)int array1[]{ 1, 2, 3, 4, 5 };int array2[5]{ 0 };vector<int> v1{ 1,2,3,4,5 };vector<int> v2 = { 1,2,3,4,5 };list<int> l1{ 1,2,3,4,5 };list<int> l2 = { 1,2,3,4,5 };map<string, int> m{ {"苹果", 1},{"西瓜",3},{"香蕉",2}};//map的每个对象都是pairmap<string, int> m1 = { {"苹果", 1},{"西瓜",3},{"香蕉",2} };//对于类的构造函数的初始化Point p1(1, 2); //正常情况下支持的Point p2{ 1, 2 };Point p3 = { 1, 2 };//c++11中的列表初始化也可用于new表达式int* pa = new int[4] {0};return 0;
}

2.2 std::initializer_list

①、类型(其为一个类模板) 

template<class T> class initializer_list;

②、使用场景

std::initializer_list可以认为是一个容器,其 一般作为构造函数的参数,C++11 STL中的不少容器就增加std::initializer_list 作为参数的构造函数,这样初始化容器对象就更方便了。也可以作为operator=的参数,这样就可以用大括号赋值。
//initializer_list的使用(两种写法)
auto i1 = { 10, 20, 30 }; //the type of i1 is an initializer_list
initializer_list<int> i2 = { 1, 2, 3 };//容器是如何支持这种花括号的列表初始化的呢?
//使模拟实现的vector也支持{}初始化和赋值
/*vector(initializer_list<T>l):_capacity(l.size()), _size(0)
{	//为了讲解,这里假设vector没用指针实现,而是用_capacity,_size实现_array = new T[_capacity];for (auto e : l)_array[_size++] = e;
}*/
//其他容器也类似

注:容器支持花括号列表初始化,本质是增加了一个initializer_list的构造函数,initializer_list可以接收{}列表  


 3、声明

 c++11提供了多种简化声明的方式,尤其是在使用模板时。

 3.1 auto和范围for

C++98 auto 是一个存储类型的说明符,表明变量是局部自动存储类型,但是局部域中定义局
部的变量默认就是自动存储类型,所以 auto 就没什么价值了。 C++11 中废弃 auto 原来的用法,将
其用于实现自动类型推断。这样要求必须进行显式初始化,让编译器将定义对象的类型设置为初
始化值的类型。
//auto和范围for(熟悉)->简化了代码的写法//auto不能做形参和返回值
//auto func(auto e)
//{}
int main()
{std::map<std::string, std::string> dict = { {"leverage","影响力"}, {"acre", "英亩"} };std::map<std::string, std::string>::iterator it1 = dict.begin();auto it2 = dict.begin();//用auto明显更方便书写了//注:这里当容器存的对象比较大时或这个对象要做深拷贝,如string//最好给引用和const,可以减少拷贝,提高效率for (const auto& e : dict) {//容器支持范围for原理:范围for会被编译器替换成迭代器,则支持迭代器就支持范围forcout << e.first << e.second << endl;//acre英亩\nleverage影响力}//注:auto生成的迭代器是可以当参数进行传参的//因为auto生成对象跟只用类型对象是一样的,即it1与it2是一样的,没有区别   //唯一区别:it2类型是编译器自动推导出来的//auto的优势就是可以把类型比较复杂的地方,简化代码的写法//除了STL的容器可以用范围for用法,数组也可以(原生指针也可以认为是天然迭代器,如vector//string等迭代器就是原生指针int a[] = { 1,2,3,4,5,6 };for (auto e : a){cout << e << " ";}cout << endl;
}

3.1decltype

关键字 decltype 将变量的类型声明为表达式指定的类型
//类型推导,属于RTTI (run time type identification)【了解】
//程序运行时对象的类型识别
int main()
{int a = 10;int b = 20;double c = 10;auto d = a + b;auto e = a + c;//拿到类型名称的字符串cout << typeid(d).name() << endl; //intcout << typeid(e).name() << endl; //doublestring s;cout << typeid(s).name() << endl; //class std::basic_string<char,....>//若想定义一个跟d一样类型的对象//typeid(d).name() f;   //报错,故用decltype//通过对象去推类型decltype(e) g;decltype(e) h;cout << typeid(g).name() << endl; //doublecout << typeid(h).name() << endl; //doublereturn 0;
}

3.3 nullptr


 4、新容器

容器中的一些新方法
如果我们再细细去看会发现基本每个容器中都增加了一些C++11的方法,但是其实很多都是用得比较少的。 比如提供了 cbegin cend 方法返回 const 迭代器等等,但是实际意义不大,因为 begin end也是可以返回 const 迭代器的,这些都是属于锦上添花的操作。
实际上 C++11 更新后,容器中增加的新方法最后用的插入接口函数的右值引用版本。
但是这些接口到底意义在哪?网上都说他们能提高效率,他们是如何提高效率的?
请看下面的右值引用和移动语义的讲解。另外 emplace还涉及模板的可变参数,也需要再继续深入学习后面的知识。

5、 右值引用

5.1左值引用和右值引用

传统的C++语法中就有引用的语法,而C++11中新增了的右值引用语法特性,所以我们之前学习的引用就叫左值引用。无论左值引用还是右值引用,都是给对象取别名。不过左值引用主要给左值取别名,右值引用主要给右值取别名

什么是左值?什么是左值引用?
左值是一个表示数据的表达式 ( 如变量名或解引用的指针 ) 我们可以获取它的地址 + 可以对它赋
值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边 。定义时 const 修饰符后的左
值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
// 以下的p、b、c、*p都是左值
int* p = new int(0);
int b = 1;
const int c = 2;
// 以下几个是对上面左值的左值引用int*&rp=p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;

什么是右值?什么是右值引用?

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

// 以下几个都是常见的右值
10;
x + y;
fmin(x, y);// 以下几个都是对右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);// 这里编译会报错:error C2106: “=”: 左操作数必须为左值
10=1;
x + y = 1;
fmin(x, y) = 1;
需要注意的是右值是不能取地址的,但是给右值取别名后,会导致右值被存储到特定位置,且可
以取到该位置的地址,也就是说例如:不能取字面量 10 的地址,但是 rr1 引用后,可以对 rr1 取地
址,也可以修改 rr1 。如果不想 rr1 被修改,可以用 const int&& rr1 去引用,是不是感觉很神奇,
这个了解一下实际中右值引用的使用场景并不在于此,这个特性也不重要。
double x = 1.1, y = 2.2;
int&& rr1 = 10;
const double&& rr2 = x+y;
rr1 = 20;
rr2 = 5.5;  // 报错

5.2 左值引用与右值引用比较 

 左值引用总结:

1. 左值引用只能引用左值,不能引用右值。
2. 但是 const 左值引用既可引用左值,也可引用右值。(这也说明了我们建议加const的原因之一)
//左值引用不能直接引用右值,const左值引用既可以引用左值,也可引用右值
//int& e = 10;
//int& f = x + y;
const int& e = 10;
const int& f = x + y;
右值引用总结:
1. 右值引用只能右值,不能引用左值。
2. 但是右值引用可以 move 以后的左值。
按照语法,右值引用只能引用右值,但右值引用一定不能引用左值吗?因为:有些场景下,可能 真的需要用右值去引用左值实现移动语义。 当需要用右值引用引用一个左值时,可以通过 move 函数将左值转化为右值 C++11 中, std::move() 函数位于 头文件中,该函数名字具有迷惑性,它 并不搬移任何东西,唯一的功能就是将一个左值强制转化为右值引用,然后实现移动语义
//右值引用只能引用右值,不能引用左值,但可以引用move后的左值
//error C2440: “初始化”: 无法从“int”转换为“int &&”
//message : 无法将左值绑定到右值引用
//int&& mm = a;
int&& mm = move(a);

5.3 左值和右值引用使用场景及意义

前面我们可以看到左值引用既可以引用左值和又可以引用右值(加const才行),那为什么 C++11还要提出右值引用呢?下面我们来看看左值引用的短板,右值引用是如何补齐这个短板的。
左值引用的使用场景:
做参数和做返回值都可以提高效率。
void func1(string s)
{}
void func2(const string& s)
{}
int main()
{string s1("hello world");// func1和func2的调用我们可以看到左值引用做参数减少了拷贝,提高效率的使用场景和价值func1(s1);func2(s1);// string operator+=(char ch) 传值返回存在深拷贝// string& operator+=(char ch) 传左值引用没有拷贝提高了效率s1 += '!';return 0;
}
左值引用的短板:
但是当函数返回对象是一个局部变量,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。例如: string to_string(int value)函数中可以看到,这里只能使用传值返回,传值返回会导致至少 1 次拷贝构造 ( 如果是一些旧一点的编译器可能是两次拷贝构造 )
这里用string中的to_string来说明这个问题:
namespace mz
{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()
{// 在mz::string to_string(int value)函数中可以看到,这里只能使用传值返回,// 传值返回会导致至少1次拷贝构造(如果是一些旧一点的编译器可能是两次拷贝构造)。string ret1 = mz::to_string(1234);string ret2 = mz::to_string(-1234);cout << ret1 << endl; //1234cout << ret2 << endl; //-1234return 0;
}


右值引用的使用场景:

场景1(简单应用)

template<class T>
void f(T& a)
{cout << "void f(const T& a)" << endl;
}template<class T>
void f(T&& a)
{cout << "void f(const T&& a)" << endl;
}int main()
{int x = 10;//f的左值右值引用的参数不同,故构成函数重载f(x);  //void f(const T& a)  ->匹配左值引用f(10); //void f(const T&& a) ->匹配右值引用return 0;
}

场景2

C++11又将右值区分为:纯右值和将亡值
纯右值:基本类型的常量或者临时对象
将亡值:自定义类型的临时对象

以我们模拟实现的String为例,看看右值引用的应用场景 

class String
{
public:String(const char* str = " "){_str = new char[strlen(str) + 1];strcpy(_str, str);}//s2(s1)String(const String& s){cout <<"String(const String& s)-拷贝构造-效率低" << endl;_str = new char[strlen(s._str) + 1];strcpy(_str, s._str);}//s3(右值-将亡值)String(String&& s):_str(nullptr){//传进来的是个将亡值,反正你都要亡了,我的目的是跟你有一样大的空间//不如把你的空间给我cout << "String(String&& s)-移动拷贝构造-效率高" << endl;swap(_str, s._str);}//s3 = s4String& operator=(const String& s){cout << "String& operator=(const String& s)-拷贝赋值-效率低" << endl;if (this != &s){char* newstr = new char[strlen(s._str) + 1];strcpy(newstr, s._str);delete[] _str;_str = nullptr;}return *this;}//s3 = 右值-将亡值String& operator=(String&& s){cout << "String& operator=(const String&& s)-移动赋值-效率高" << endl;swap(_str, s._str); //直接掠夺s的资源即可,反正你s都要亡了return *this;}//第二个层次的问题//s1 + s2String operator+(const String& s2){String ret(*this);//ret.append(s2._str); //因append我们没有模拟实现,故这里注释掉return ret;	//返回的是右值}//s1 += s2String& operator+=(const String& s2){//this->append(s2);return *this; //返回的是左值}~String(){delete[]_str;}
private:char* _str;
};String f(const char* str)
{String tmp(str);return tmp; // 返回tmp拷贝的临时对象
}int main()
{String s1("左值");String s2(s1);				 //参数是左值//String s3(String("右值"));  //编译器会优化掉String s3(f("右值-将亡值"));	  //参数是右值-将亡值(传给你用,用完我就析构了),但编译器也会优化掉String s4(move(s1));			String s5("zuo值");s5 = s1;String s6("s6");String s7("s7");String s8 = s6 += s7; //拷贝构造String s9 = s6 + s7;  //移动构造return 0;

①、拷贝构造和移动拷贝构造的场景 (可以减少拷贝)

移动构造本质是将参数右值的资源窃取过来,占位已有,那么就不 用做深拷贝了,所以它叫做移动构造,就是窃取别人的资源来构造自己

 注:正常的左值引用时拷贝构造,而右值引用是移动拷贝构造

 ②、拷贝赋值和移动赋值的场景与①类似(也可以减少拷贝)

③、左值和右值做返回值的场景

 operator+与operator+=的返回值

注:operator+返回的是一个右值,因为既有拷贝构造又有移动构造,编译器会选择最匹配的参数调用,用这个右值构造s9,就会匹配调用移动构造。这里其实是个移动语义。

 总结:

以前我们写的函数总是避免用传值做返回值,因为会有深拷贝,但这里有右值引用后,会有移动构造和移动赋值减少拷贝,即不会深拷贝了,那你想传值返回就传值返回(很便利)

 ④、所有深拷贝类(vector/list/map/set..),都可以加两个右值引用做参数的移动拷贝和移动赋值 

  

总结:

⑤、右值引用做函数参数,减少拷贝 

 

 ⑥、左值引用和右值引用减少拷贝的对比


 6、完美转发

模板中的&& 万能引用

解决:利用完美转发,std::forward 完美转发在传参的过程中保留对象原生类型属性

完美转发实际中的使用场景:
list的模拟实现中
template<class T>
struct ListNode
{ListNode* _next = nullptr;ListNode* _prev = nullptr;T _data;
};
template<class T>
class List
{typedef ListNode<T> Node;
public:List(){_head = new Node;_head->_next = _head;_head->_prev = _head;}void PushBack(T&& x){//Insert(_head, x);Insert(_head, std::forward<T>(x));}void PushFront(T&& x){//Insert(_head->_next, x);Insert(_head->_next, std::forward<T>(x));//复用Insert中可能丢失右值属性,故完美转发}void Insert(Node* pos, T&& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = std::forward<T>(x); // 关键位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}void Insert(Node* pos, const T& x){Node* prev = pos->_prev;Node* newnode = new Node;newnode->_data = x; // 关键位置// prev newnode posprev->_next = newnode;newnode->_prev = prev;newnode->_next = pos;pos->_prev = newnode;}
private:Node* _head;
};
int main()
{List<bit::string> lt;lt.PushBack("1111");lt.PushFront("2222");return 0;
}

7、新的类功能

7.1默认成员函数

原来C++类中,有6个默认成员函数:
1. 构造函数
2. 析构函数
3. 拷贝构造函数
4. 拷贝赋值重载
5. 取地址重载
6. const 取地址重载
最后重要的是前 4 个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。
C++11 新增了两个:移动构造函数和移动赋值运算符重载。
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
①、 如果你没自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任
意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类
型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,
如果实现了就调用移动构造,没有实现就调用拷贝构造。
②、 如果你没自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中
的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内
置类型成员会执行逐成员按字节拷贝自定义类型成员,则需要看这个成员是否实现移动赋
值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。 ( 默认移动赋值跟上面移动构造
完全类似 )
③、 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。

7.2强制生成默认函数的关键字default:

C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原 因这个函数没有默认生成。
①、有拷贝构造导致的编译器无法生成默认构造函数
class A
{
public:A(const int& a):_a(a){}//因为你写了拷贝构造,编译器就不会生成默认构造了//法一、自己写一个默认构造//法二、default:指定编译器显式的生成(C++11做法)A() = default; private:int _a = 10;
};int main()
{A aa1;     //若不用default,则失败A aa2(aa); //成功return 0;
}

②、有拷贝构造导致的编译器无法生成移动构造

我们可以 使用 default关键字显示指定移动构造生成
class Person
{
public:Person(const char* name = "", int age = 0):_name(name), _age(age){}Person(const Person& p):_name(p._name), _age(p._age){}Person(Person&& p) = default;
private:string _name;int _age;
};
int main()
{Person s1;Person s2 = s1;Person s3 = std::move(s1);return 0;
}

7.2禁止生成默认函数的关键字delete:

如果能想要限制某些默认函数的生成,在 C++98 中,是该函数设置成 private ,并且只声明补丁
已,这样只要其他人想要调用就会报错。在 C++11 中更简单,只需在该函数声明加上 =delete
可,该语法指示编译器不生成对应函数的默认版本,称 =delete 修饰的函数为删除函数。
class A
{
public:A() = default; //若要求A的对象不能拷贝和赋值(防拷贝)//C++98的做法:只给声明,不给实现,这样别人就无法拷贝对象//缺陷:导致链接不上,且别人可以再类外定义
//	A(const int& a); 
//	A& operator=(const A& aa);//为解决上面缺陷,private限定,类外也无法定义
//private:
//	A(const int& a);
//	A& operator=(const A& aa);//C++11的做法:用delete定义为删除函数A(const int& a) = delete;A& operator=(const A& aa) = delete;A(const int& a):_a(a){}private:int _a = 10;
};int main()
{A aa1;A aa2(aa1);aa1 = aa2;return 0;
}

C++【下】链接:【C++】C++11【下】lambda表达式|thread线程库-CSDN博客

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

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

相关文章

2023年【低压电工】考试及低压电工模拟考试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 低压电工考试根据新低压电工考试大纲要求&#xff0c;安全生产模拟考试一点通将低压电工模拟考试试题进行汇编&#xff0c;组成一套低压电工全真模拟考试试题&#xff0c;学员可通过低压电工模拟考试题全真模拟&#…

2023/10/29总结

总结 踩坑记录 写代码的时候遇到了一个错误大概是这样的 io.jsonwebtoken.security.WeakKeyException: The signing keys size is 48 bits which is not secure enough for the HS256 algorithm. The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used…

pycharm使用ssh连接远程jupyter

1. 安装jupyter pip install jupyter2. 生成jupyter_notebook_config.py文件 jupyter notebook --generate-config3. 设置命令参数 jupyter notebook --no-browser --allow-root --port 8900配置Jupyter服务器 将上面的代码复制到命令行实参中&#xff1a;

管理类联考——数学——汇总篇——知识点突破——代数——函数、方程——记忆

文章目录 考点记忆/考点汇总——按大纲 整体局部 本篇思路&#xff1a;根据各方的资料&#xff0c;比如名师的资料&#xff0c;按大纲或者其他方式&#xff0c;收集/汇总考点&#xff0c;即需记忆点&#xff0c;在通过整体的记忆法&#xff0c;比如整体信息很多&#xff0c;通常…

Day17力扣打卡

打卡记录 参加会议的最多员工数&#xff08;拓扑排序 分类讨论&#xff09; 链接 计算内向基环树的最大基环&#xff0c;基环树基环为2的情况分类讨论。 class Solution { public:int maximumInvitations(vector<int> &favorite) {int n favorite.size();vector…

动作捕捉系统进行坐标系转换

动作捕捉系统在机器人等应用中常出现被测物与动捕坐标系不一致的问题。这时就需要进行坐标系的转换。在NOKOV度量动作捕捉系统软件中&#xff0c;可以对被测物的坐标系原点偏移量进行设置&#xff0c;实现被测物坐标系与大地坐标系的重合。 一、坐标系偏移操作 在形影动捕软件…

IOI车机系统刷机和改包笔记2 - 改包脚本

前言&#xff1a; 最近对雪佛兰改包需求感兴趣的网友很多&#xff0c;大家都遇上了很多奇怪的问题&#xff0c;这里就我自己使用的环境和脚本进行分享&#xff0c;供大家参考。 1. 准备环境 我这里使用Ubuntu系统进行操作 andyandy-vm:~$ sudo lsb_release -a No LSB module…

影视企业有哪些方式将视频文件快速海外跨国传输国内?

影视行业是一个高度国际化的行业&#xff0c;影视企业在跨国合作、制作、发行等方面有着强烈的需求。然而&#xff0c;影视企业在跨国文件传输方面也面临着诸多的问题和难题。视频文件通常具有较大的文件大小、多样的文件格式、高要求的文件质量等特点&#xff0c;这些特点使得…

【Midjourney入门教程4】与AI对话,写好prompt的必会方法

文章目录 1、语法2、单词3、要学习prompt 框架4、善用参数&#xff08;注意版本&#xff09;5、善用模版6、临摹7、垫图 木匠不会因为电动工具的出现而被淘汰&#xff0c;反而善用工具的木匠&#xff0c;收入更高了。 想要驾驭好Midjourney&#xff0c;可以从以下方面出发调整&…

代码随想录 Day35 动态规划04 01背包问题和完全背包问题 LeetCode T416 分割等和子集

背包问题 说到背包问题大家都会想到使用动规的方式来求解,那么为什么用动规呢,dp数组代表什么呢?初始化是什么,遍历方式又是什么,这篇文章笔者将详细讲解背包问题的经典例题0-1背包问题和完全背包问题的解题方式,希望能帮助到大家 1.暴力方式 有人一提到背包问题就只会使用动态…

Mybatis学习

一、 1.第一个mybatis程序 层层递进&#xff0c;SqlSession用来执行sql语句&#xff0c;SqlSession是与数据库的一次会话。 通过SqlSessionFactory获取SqlSession 通过SqlSessionBuilder的build()方法获取SessionFactory 2.第一个程序就找了30分钟的错&#xff08;悲惨&…

3.字符集和比较规则简介

3.字符集和比较规则简介 1.字符集和比较规则简介1.1 字符集简介1.2 比较规则简介1.3 一些重要的比较规则 2. MySQL 中支持的字符集和比较规则2.1 MySQL 的 utf8 和 utf8mb42.2 字符集查看2.3 比较规则查看 3. 字符集和比较规则的应用3.1 各级别的字符集和比较规则1. 服务器级别…