1.更新后的一些小的操作
struct point
{
int _a;
int _b;
};
class Date
{
public:
int year;
int mon;
int day;
Date(int y, int m, int d) : year(y), mon(m), day(d) {}
};
void test_11()
{
int i1 = 0;
int arr[5] = { 0 };
point p = { 1,2 };//这种初始化叫做-----列表初始化!
//c++11更新后-省略了赋值括号!一切皆可以用花括号初始化!
int i = { 0 };
int j{ 0 };
point p1{ 0 ,1};
int arr1[5]{ 0 };
//这个属于是构造加拷贝构造
Date d1(2025, 3, 27);
//这块c11就属于是优化为直接构造
Date d2{ 2025, 3, 27 };
//假如我现在想创建一个-新的类对象
Date* p2 = new Date[3]{ d1,d2,d1 };//这c++
// 下面是更新后的----它可以直接在初始化的时候创建一个临时对象
Date* pp = new Date[3]{ {2021,3,3},{2022,3,3},{2023,3,3} };
// 容器初始化更新后
vector<int> v{ 1,2,2,3 };
list<int> l{ 1,1,1,1 };
//c++11更新了一种新的类型叫做initializer_list
auto il = { 1,334,4,54546,657 };//注意它虽然叫list但是不是链表
cout << typeid(il).name() << endl;
std::initializer_list<int>::iterator it = il.begin();
while (it!=il.end())
{
cout << *it << endl;
++it;
}
map<string, int> m{ {"sort",1},{"get",1} };
for (auto e : m)
{
cout << e.first << ": " << e.second << endl;
}
/*有些时候我们需要推到出一些返回值的类型,目前我们常用到的就是typeid().name()
但是假如我们需要用一个变量的类型去定义一个vector数组,typeid就不行了,所以c++11此时推出了decltype()*/
int i = 1;
double g = 2.2;
auto ret = i * j;
vector<decltype(ret)> vv;//就像这样使用。
//它可以用来推到对象的类型,这个类型可以用来模版实参,或者再进行定义对象!
decltype(ret) k;
//引用的特点就是减少拷贝!
//左值引用能否给右值取别名!不能,但是加上const就可以---(左值引用是一个&)
const int& rr = i + g;
//右值引用能否给右值取别名,当然是可以的-----(右值引用是两个&)
int&& r = i + g;
//右值引用能否给左值取别名?不能但是右值引用可以给move(左值)取别名 move函数将左值转换为右值,然后让右值引用绑定
int&& rr3 = move(i);
//数组函数array
array<int, 10> a;//array实际开空间是开在栈上的,vector开空间是开在堆上的,栈很小堆很大!
//上下两行效果一样!
vector<int> k(10, 0);//10个空间
// 右值不能出现在操作符左边而且不能被修改,而左值既可以在左边也可以在右边主要取决于看你能否取到地址
}
2.右值引用和移动语义
左值引用和右值引用
传统的 C++ 语法中就有引用的语法,而 C++11 中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。
什么是左值?什么是左值引用?
左值是一个表示数据的表达式 (如变量名或解引用的指针),我们可以获取它的地址,一般可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时 const 修饰符后的左值,不能给他赋值,但是可以取它的地址。左值引用就是给左值的引用,给左值取别名。
这个结论可以联想到具名对象!!!
class LargeObject
{
private:
std::string data;
public:
// 构造函数
LargeObject(const std::string& str) : data(str) {}
// 拷贝构造函数
LargeObject(const LargeObject& other) : data(other.data)
{
std::cout << "Copy constructor called" << std::endl;
}
// 移动构造函数
//这里的 other 虽然是一个右值引用,但它本身是一个具名对象,在 C++ 里,具名的右值引用会被当作左值。
LargeObject(LargeObject&& other) noexcept : data(std::move(other.data))
{
std::cout << "Move constructor called" << std::endl;
}
// 拷贝赋值运算符
LargeObject& operator=(const LargeObject& other)
{
if (this != &other)
{
data = other.data;
std::cout << "Copy assignment operator called" << std::endl;
}
return *this;
}
// 移动赋值运算符
LargeObject& operator=(LargeObject&& other) noexcept
{
if (this != &other)
{
data = std::move(other.data);
std::cout << "Move assignment operator called" << std::endl;
}
return *this;
}
};
int main()
{
std::vector<LargeObject> vec;
// 插入临时对象,调用移动构造函数
vec.push_back(LargeObject("Large data"));
vector<Date> v;
v.push_back(Date(2001, 2, 3));
v.push_back(Date(2025, 2, 3));
v.push_back(Date(1990, 2, 3));
/* for (auto e : v)
{
cout << e << " ";
}*/
PerfectForward(10);
int j = 8;
PerfectForward(j);
return 0;
}
3.禁止生成默认函数的关键字 delete
如果想要限制某些默认函数的生成,在 C++98 中,是将该函数设置成 private,并且只声明不定义,这样只要其他人想要调用就会报错。在 C++11 中更简单,只需在该函数声明加上= delete
即可,该语法指示编译器不生成对应函数的默认版本,称= delete
修饰的函数为删除函数。(当你使用的default的时候是显示的告诉编译器要自动调用默认构造!)
class Person
{
public:
Person(const char* name = "", int age = 0)
:_name(name)
, _age(age)
{}
Person(const Person& p) = delete;
};
4.可变参数模板
C++11 的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比 C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。然而由于可变模版参数比较抽象,使用起来需要一定的技巧,所以这块还是比较晦涩的。现阶段呢,我们掌握一些基础的可变参数模板特性就够我们用了,所以这里我们点到为止,以后大家如果有需要,再可以深入学习。
下面就是一个基本可变参数的函数模板:
这个print可以帮助你把传入的数据给打印出来!
template <class T>
int PrintArg(T t)
{
cout << t << " ";
return 0;
}
//展开函数
template <class...Args>
void ShowList(Args... args)
{
int arr[] = { PrintArg(args)... };
cout << endl;
}
int main()
{
ShowList(1);
ShowList(1, 'A');
ShowList(1, 'A', std::string("sort"));
return 0;
}
5.push_back
和emplace_back
首先push_back是构造加拷贝构造
emplace_back是构造加移动构造--处理对象比较大的时候比较好