衡水做网站改版,千万不要学建筑设计,网络推广优化平台,wordpress 极验证1.列表初始化
C98传统的{}
C98中一般数组和结构体可以用{}进行初始化。
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;
}
C11中的{}
C11以后统一初始化方式#xff0c;想要实现一切对…1.列表初始化
C98传统的{}
C98中一般数组和结构体可以用{}进行初始化。
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;
}
C11中的{}
C11以后统一初始化方式想要实现一切对象皆可用{}初始化{}初始化也叫做列表初始化
内置类型支持自定义类型也支持自定义类型本质是类型转换中间会产生临时对象最后优化了以后就只有构造
{}初始化过程可以省略
代码示例
#includeiostream
#includevector
using namespace std;struct Point
{int _x;int _y;
};class Date
{
public:Date(int year 1, int month 1, int day 1):_year(year),_month(month),_day(day){cout Date(int year,int month,int day) endl;}Date(const Date d):_year(d._year),_month(d._month),_day(d._day){cout Date(const Date d) endl;}private:int _year;int _month;int _day;
};int main()
{//C11支持//内置类型支持int x1 { 2 };//自定义类型支持//这里本质是用{202511}构造一个Date临时对象//临时对象再去拷贝构造d1编译器优化后会合二为一变成{202511}直接构造Date d1 { 2025,1,1 };//这里d2引用的是{2024725}构造的临时对象const Date d2 { 2024,7,25 };//C98支持单参数类型转换也可以不用{}Date d3 { 2025 };Date d4 2025;//可以省略掉Point p1{ 1,2 };int x2{ 2 };Date d6{ 2024,7,25 };const Date d7{ 2024,7,25 };//只有{}初始化才能省略//不支持 Date d8 2025vectorDate v;v.push_back(Date(2025, 1, 1));v.push_back(d1);//对于匿名对象和匿名对象传参这里{}相对好v.push_back({ 2025,1,1 });return 0;} C11中std::initializer_list
上面初始化很方便当对于容器的初始化还是不方便像vector对象想用N个值去构造初始化就得实现很多个构造函数才能支持 vectorint v1 {1,2,3};vectorint v2 {1,2,3,4,5}; C11库中提出了一个std::initializer_list的类 auto il { 10, 20, 30 }; // the type of il is an initializer_list这个类本质是底层开一个数组将数据拷贝过来std::initializer_list内部有俩个指针分别指向数组的开始和结束。 容器支持一个std::initializer_list的构造函数也就支持任意多个值构成的{x1,x2,x3....}进行初始化。STL中的容器支持任意多个值构成的{x1,x2,x3...}进行初始化就是通过std::initializer_list的构造函数支持的。 // STL 中的容器都增加了⼀个 initializer_list 的构造 vector (initializer_listvalue_type il, const allocator_type alloc allocator_type ()); list (initializer_listvalue_type il, const allocator_type alloc allocator_type ()); map (initializer_listvalue_type il, const key_compare comp key_compare (), const allocator_type alloc allocator_type ()); // ... template class T class vector { public : typedef T* iterator; vector (initializer_listT l) { for ( auto e : l) push_back (e) } private : iterator _start nullptr ; iterator _finish nullptr ; iterator _endofstorage nullptr ; }; // 另外容器的赋值也⽀持 initializer_list 的版本 vector operator (initializer_listvalue_type il); map operator (initializer_listvalue_type il); # include vector # include string # include map using namespace std; int main () { std::initializer_list int mylist; mylist { 10 , 20 , 30 }; cout sizeof (mylist) endl; // 这⾥ begin 和 end 返回的值 initializer_list 对象中存的两个指针 // 这两个指针的值跟 i 的地址跟接近说明数组存在栈上 int i 0 ; cout mylist. begin () endl; cout mylist. end () endl; cout i endl; // {}列表中可以有任意多个值 // 这两个写法语义上还是有差别的第⼀个 v1 是直接构造 // 第⼆个 v2 是构造临时对象 临时对象拷⻉ v2 优化为直接构造 vector int v1 ({ 1 , 2 , 3 , 4 , 5 }); vector int v2 { 1 , 2 , 3 , 4 , 5 }; const vector int v3 { 1 , 2 , 3 , 4 , 5 }; // 这⾥是 pair 对象的 {} 初始化和 map 的 initializer_list 构造结合到⼀起⽤了 mapstring, string dict { { sort , 排序 }, { string , 字符串 }}; / / initializer_list版本的赋值⽀持 v1 { 10 , 20 , 30 , 40 , 50 }; return 0 ; } 右值引用和移动语义
C98就有引用在C11新增了右值引用语法特性C11之后之前的学的就叫左值引用。无论左值还是右值引用都是给对象取别名。
左值和右值
左值是一个表示数据的表达式一般是有持久状态存储在内存中可以获取1它的地址左值可以出现在赋值符号的左边也可以出现在右边。定义时const修饰符后的左值不能给它赋值可以取地址。
右值也是一个表示数据的表达式要么是字面值常量要么是表达式求值过程中创建的临时对象等右值可以出现在赋值符号的右边但是不能出现在左边右值不能取地址。
左右值核心区别在于能不能取地址。 代码示例
#includeiostream
using namespace std;int main()
{//以下pbc,*p,s,s[0]就是常见的左值int* p new int(0);int b 1;const int c b;*p 10;string s(111111);s[0] x;cout s endl;cout (void*)s[0] endl;//右值不能取地址double x 1.1, y 2.2;10;x y;fmin(x, y);//参数为浮点数返回较小的浮点数string(111111);return 0;
}
补充用(void*)强制转换是为了打印出地址没有就会打印出值 左值引用和右值引用
Type% r1x;Type rr1y; 第一个语句就是左值引用左值引用就是给左值取别名右值引用就是给右值取别名。
左值引用不能直接引用右值但是const左值可以引用右值。
右值引用不能直接引用左值但是右值可以引用move(左值) template class T typename remove_referenceT::type move (T arg); move是库里面的一个函数模板本质内部是进行强制类型转换。
需要注意变量表达式都是左值属性也就说一个右值被右值引用绑定后右值引用变量表达式的属性是左值。
代码示例
#includeiostreamusing namespace std;int main()
{int* p new int(0);int b 1;const int c b;*p 10;string s(11111);s[0] x;double x 1.1, y 2.2;//左值引用给左值取别名int r1 b;int* r2 p;int r3 *p;string r4 s;char r5 s[0];//右值引用给右值取别名int rr1 10;double rr2 x y;//表达式返回值是临时变量double rr3 fmin(x, y);//返回值临时变量string rr4 string(11111);//匿名对象活不过下一行//左值引用不能直接引用右值但是const左值可以引用右值const int rx1 10;const double rx2 x y;const double rx3 fmin(x, y);const string rx4 string(11111);//右值引用不能直接用于左值但是可以引用move(左值)int rrx1 move(b);int* rrx2 move(p);int rrx3 move(*p);string rrx4 move(s);string rrx5 (string)s;//跟move(s)一样//int r110,此时rr1的属性是左值所以不能再被右值引用绑定除非moveint r6 rr1;//因为rr1的属性是左值所以左值引用不用加const就可以引用rr1//int rrx6rr1 这里是不行的右值不能直接引用左值int rrx6 move(rr1);return 0;
} 引用延长生命周期
右值引用可以为临时变量延长生命周期const的左值引用也可以延长临时对象生命周期但是这些对象无法修改。 代码示例
int main()
{std::string s1 Test;const std::string r2 s1 s1;//const的左值引用延长了生命周期但是不能被修改std::string r3 s1 s1;//右值引用延长生命周期r3 Test;//能通过非constreturn 0;
}
左值和右值的参数匹配
在C98中实现一个const左值引用作为参数的函数则实参传递左值和右值都可以匹配。
C11后分别重载左值引用const左值引用右值引用作为形参的f函数可以试验前面的结论
代码示例
#includeiostreamusing namespace std;void f(int x)
{cout 左值引用重载\n endl;
}void f(const int x)
{cout const左值引用重载\n endl;
}void f(int x)
{cout 右值引用重载\n endl;
}int main()
{int i 1;const int ci 2;f(i);//f(int)f(ci);//f(const int)f(3);//f(int),没有f(int)就会走f(const int)f(move(i));//f(int)int x 1;f(x);//右值引用在用于表达式时是左值f(move(x));//f(int)return 0;
}在 C 中右值引用可以延长生命周期的原因在于它们专门设计用来处理临时对象右值而左值引用则是用来引用可以持续存在于内存中的对象左值。以下是为什么右值引用可以延长生命周期而左值引用不可以的详细解释 ### 右值引用延长生命周期的原因 1. **临时对象的生命周期** - 临时对象右值通常在表达式求值结束后立即销毁。右值引用通过绑定到这些临时对象可以延长它们的生命周期使得它们可以被多次使用直到右值引用本身被销毁或再次赋值。 2. **移动语义** - 右值引用是实现移动语义的关键。移动语义允许资源如动态内存、文件句柄等从一个地方“移动”到另一个地方而不是复制。这种转移是通过右值引用实现的它允许临时对象的资源被转移给另一个对象从而延长了资源的生命周期。 3. **优化和性能** - 使用右值引用可以避免不必要的复制提高程序性能。例如在函数返回临时对象时如果使用右值引用可以直接将资源转移给调用者而不是创建一个临时对象的副本。 ### 左值引用不延长生命周期的原因 1. **持久性** - 左值引用是设计用来引用具有持久存储的对象。它们指向对象的身份而不是值。左值引用的目的是提供对对象的直接访问而不是延长其生命周期。 2. **别名问题** - 如果左值引用可以延长临时对象的生命周期那么它可能会引入别名问题aliasing。例如如果两个左值引用指向同一个对象对其中一个引用的修改可能会影响另一个引用这在很多情况下是不希望发生的。 3. **语义清晰** - 左值引用和右值引用有不同的语义。左值引用意味着对对象的持久引用而右值引用意味着对临时对象的引用。这种区分有助于编译器优化和程序员理解代码。 4. **避免悬挂引用** - 如果左值引用可以延长临时对象的生命周期那么在对象被销毁后引用仍然存在这将导致悬挂引用dangling reference即引用指向一个已经不再有效的内存区域。 总结来说右值引用可以延长生命周期是因为它们专门设计用来处理临时对象并且是实现移动语义的关键。而左值引用不延长生命周期是因为它们设计用来引用持久存储的对象并且延长临时对象的生命周期可能会导致别名问题和悬挂引用等问题。这种设计使得左值引用和右值引用在语义上保持清晰有助于编译器优化和代码理解。 右值引用和移动语义的使用场景
左值引用主要使用场景
左值引用主要使用场景是在函数中左值引用传参和左值引用返回值时减少拷贝同时还可以修改实参和修改返回对象的值。左值引用已经解决大多数场景的拷贝效率问题但是有些场景不能使用传左值引用返回如addStrings和generate函数。而C11以后的右值引用也不可以解决这里本质是返回对象是一个局部对象函数结束这个对象就析构销毁也就是给了一个野指针。
代码示例 class Solution {
public:// 传值返回需要拷⻉string addStrings(string num1, string num2) {string str;int end1 num1.size() - 1, end2 num2.size() - 1;// 进位int next 0;while (end1 0 || end2 0){int val1 end1 0 ? num1[end1--] - 0 : 0;int val2 end2 0 ? num2[end2--] - 0 : 0;int ret val1 val2 next;next ret / 10;ret ret % 10;str (0 ret);}if (next 1)str 1;reverse(str.begin(), str.end());return str;}
};
class Solution {
public:// 这⾥的传值返回拷⻉代价就太⼤了vectorvectorint generate(int numRows) {vectorvectorint vv(numRows);for (int i 0; i numRows; i){vv[i].resize(i 1, 1);}for (int i 2; i numRows; i){for (int j 1; j i; j){vv[i][j] vv[i - 1][j] vv[i - 1][j - 1];}}return vv;}
};
移动构造和移动赋值
移动构造函数是一种构造函数类似于拷贝构造移动构造函数要求第一个参数是该类类型的引用但是不同的是要求这个参数是右值引用如果还有其它参数额外参数必须有缺省值。
移动赋值是一个赋值运算符的重载它跟拷贝赋值构成函数重载类似拷贝函数移动赋值函数要求第一个参数是该类类型的引用但是不同的是要求这个参数是右值引用。
对于像string/vector这样的深拷贝的类或者包含深拷贝的成员变量的类移动构造和移动赋值才有意义因为移动构造和移动赋值的第一个参数都是右值引用类型本质是要抢夺引用的右值对象资源而不是像拷贝构造和拷贝赋值去拷贝资源。
代码示例
在参数为string s的移动构造里是通过swap函数交换俩个指向的内容效率是比拷贝高的同样移动赋值也是交换指向的内容。
#define _CRT_SECURE_NO_WARNINGS 1
#includeiostream
#includeassert.h
#includestring.h
#includealgorithm
using namespace std;
namespace bit
{class string{public:typedef char* iterator;typedef const char* const_iterator;iterator begin(){return _str;}iterator end(){return _str _size;}const_iterator begin() const{return _str;}const_iterator end() const{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){::swap(_str, s._str);::swap(_size, s._size);::swap(_capacity, s._capacity);}string(const string s):_str(nullptr){cout string(const string s) -- 拷贝构造 endl;reserve(s._capacity);for (auto ch : s){push_back(ch);}}// 移动构造string(string s){cout string(string s) -- 移动构造 endl;swap(s);}string operator(const string s){cout string operator(const string s) -- 拷⻉赋值 endl;if (this ! s){_str[0] \0;_size 0;reserve(s._capacity);for (auto ch : s){push_back(ch);}}return *this;}// 移动赋值string operator(string s){cout string operator(string s) -- 移动赋值 endl;swap(s);return *this;}~string(){cout ~string() -- 析构 endl;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];if (_str){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;}size_t size() const{return _size;}
private:char* _str nullptr;size_t _size 0;size_t _capacity 0;
};
}
int main()
{bit::string s1(xxxxx);// 拷⻉构造bit::string s2 s1;// 构造移动构造优化后直接构造bit::string s3 bit::string(yyyyy);// 移动构造bit::string s4 move(s1);cout ****************************** endl;return 0;
}
右值引用和移动语义解决传值返回问题
场景一是匿名对象参数为匿名对象就会走移动拷贝直接交换指向内容,场景二是先创建了一个对象然后在用移动赋值。
namespace bit
{string addStrings(string num1, string num2){string str;int end1 num1.size() - 1, end2 num2.size() - 1;int next 0;while (end1 0 || end2 0){int val1 end1 0 ? num1[end1--] - 0 : 0;int val2 end2 0 ? num2[end2--] - 0 : 0;int ret val1 val2 next;next ret / 10;ret ret % 10;str (0 ret);}if (next 1)str 1;reverse(str.begin(), str.end());cout ****************************** endl;return str;}
}
// 场景1
int main()
{bit::string ret bit::addStrings(11111, 2222);cout ret.c_str() endl;return 0;
}
// 场景2
int main()
{bit::string ret;ret bit::addStrings(11111, 2222);cout ret.c_str() endl;return 0;
}
补充 临时变量和匿名对象区别在一个是自己创建的一个是编译器生成的 对于一个函数内部变量的生命周期如果给这个变量改变生命周期是不行的因为函数结束后会销毁栈区作为这个栈区的内部变量也会销毁被跟着带走了 延长生命周期前提是存储空间都在引用对象的存储空间不在就无法延长生命周期 把左值move后就给这个左值赋予被抢夺资源的标签