
🔥个人主页:Quitecoder
🔥专栏:c++笔记仓

1.新的类功能
默认成员函数
原来C++类中,有6个默认成员函数:
- 构造函数
- 析构函数
- 拷贝构造函数
- 拷贝赋值重载
- 取地址重载
- const 取地址重载
最后重要的是前4个,后两个用处不大。默认成员函数就是我们不写编译器会生成一个默认的。
C++11 新增了两个:移动构造函数和移动赋值运算符重载
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
- 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个(三个都没实现)。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
- 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
- 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值
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& operator=(const Person& p)
{
if (this != &p)
{
_name = p._name;
_age = p._age;
}
return *this;
}
~Person()
{}*/
private:
myown::string _name;
int _age;
};
int main()
{
Person s1;
Person s2 = s1;
Person s3 = std::move(s1);
Person s4;
s4 = std::move(s2);
return 0;
}


强制生成默认函数的关键字default
Person(Person&& p) = default;

禁止生成默认函数的关键字delete
Person(const Person& p) = delete;
该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数
2.可变参数模版
C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进
下面就是一个基本可变参数的函数模板:
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值
递归展开参数包
// 递归终止函数
template <class T>
void ShowList(const T& t)
{
cout << t << endl;
}
// 展开函数
template <class T, class ...Args>
void ShowList(T value, Args... args)
{
cout << value << " ";
ShowList(args...);
}
int main()
{
ShowList(1);
ShowList(1, 'A');
ShowList(1, 'A', std::string("sort"));
return 0;
}
-
递归终止函数
template <class T> void ShowList(const T& t) { cout << t << endl; }这个函数模板是递归终止函数。当可变参数包被展开到只剩一个参数时,调用这个函数来处理最后一个参数,并打印出它的值。
-
展开函数
template <class T, class ...Args> void ShowList(T value, Args... args) { cout << value <<" "; ShowList(args...); }这是递归展开的函数模板。该函数处理当前第一个参数
value并打印,然后通过递归调用自身来处理余下的参数包args...。
ShowList(1, 'A', std::string("sort"))**
ShowList(1, 'A', std::string("sort"))- 调用展开函数模板,处理第一个参数
1,递归调用ShowList('A', std::string("sort"))。ShowList('A', std::string("sort"))调用展开函数,处理第一个参数A,递归调用ShowList(std::string("sort"))。ShowList(std::string("sort"))调用终止函数,输出:sort
- 输出:
A sort
- 输出:
1 A sort
- 调用展开函数模板,处理第一个参数
1
1 A
1 A sort
逗号表达式展开参数包
这种展开参数包的方式,不需要通过递归终止函数,是直接在expand函数体中展开的, printarg不是一个递归终止函数,只是一个处理参数包中每一个参数的函数。这种就地展开参数包的方式实现的关键是逗号表达式。我们知道逗号表达式会按顺序执行逗号前面的表达式
template <class T>
void PrintArg(T t)
{
cout << t << " ";
}
//展开函数
template <class ...Args>
void ShowList(Args... args)
{
int arr[] = { (PrintArg(args), 0)... };
cout << endl;
}
int main()
{
ShowList(1);
ShowList(1, 'A');
ShowList(1, 'A', std::string("sort"));
return 0;
}
expand函数中的逗号表达式:(printarg(args), 0),也是按照这个执行顺序,先执行printarg(args),再得到逗号表达式的结果0。同时还用到了C++11的另外一个特性——列表初始化,通过列表初始化来初始化一个变长数组, {(printarg(args), 0)…}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc... ),最终会创建一个元素值都为0的数组int arr[sizeof…(Args)]。由于是逗号表达式,在创建数组的过程中会先执行逗号表达式前面的部分printarg(args)打印出参数,也就是说在构造int数组的过程中就将参数包展开了,这个数组的目的纯粹是为了在数组构造的过程展开参数包
template <class ...Args>
void ShowList(Args... args)
{
int arr[] = { (PrintArg(args), 0)... };
std::cout << std::endl;
}
在 ShowList 函数中,int arr[] = { (PrintArg(args), 0)... }; 语句是关键所在:
(PrintArg(args), 0)...是一个逗号表达式。它会对参数包args中的每个参数展开,并依次调用PrintArg函数打印每个参数。- 逗号表达式返回的最终值是
0,这就是每个展开的元素分别是(PrintArg(args), 0), 确保每个元素最终被转换为0并存储在int数组中。
每次调用 ShowList 都会根据提供的参数数量和类型进行相应地展开并打印。
- 逗号表达式
(PrintArg(args), 0)...允许我们对每个参数进行操作,这在展开可变参数包时非常有用。 - 初始化列表
{ ... }用来收集所有展开的结果。 - 使用
(void)强制类型转换可以避免编译器发出警告。 - 该方法使处理不定数目参数的模板函数变得简洁而高效。
3.emplace_back
template <class... Args>
void emplace_back (Args&&... args);
emplace_back 是 C++ 标准库容器(例如 std::vector, std::deque 等)的一个成员函数,它的主要作用是直接在容器末尾构造一个元素,而不是先构造一个临时对象再拷贝或移动到容器中。
container.emplace_back(arguments...);
arguments...是传递给元素构造函数的参数。
-
直接构造:
emplace_back直接在容器的内存中调用元素的构造函数。 -
支持可变参数:你可以传递多个参数,这些参数将直接传递给对象的构造函数。
-
避免意外构造:
emplace_back直接调用构造函数,因此会发生隐式类型转换。如果构造函数的参数可以匹配多个重载,可能导致意外的构造。std::vector<int> numbers; numbers.emplace_back(3.14); // 会将 3.14 隐式转换为 int,并存储为 3 -
需要构造函数支持:
emplace_back需要对应的构造函数存在。如果类没有合适的构造函数,emplace_back会无法使用。

这一部分区别不大

emplace_back是直接构造了,push_back是先构造,再移动构造

1448

被折叠的 条评论
为什么被折叠?



