最后重要的是前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修饰的函数为删除函数
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")) 调用终止函数,输出:sortA sort1 A sort1
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) 强制类型转换可以避免编译器发出警告。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是先构造,再移动构造