可变参数模板
基本概念:C++ 的参数模板是 C++11 引入的特性,它允许模板接受可变数量的参数
// Args是一个模板参数包,args是一个函数形参参数包
// 声明一个参数包Args... args,这个参数包中可以包含0到N个模板参数。
template <class ...Args>
void ShowList(Args... args)
{}
emplace_back
基本概念:emplace_back是C++11提供的一个基于可变参数模板实现的新的成员函数
我们用下面这段代码来尝试一下emplace_back和push_back的区别:
///验证深拷贝
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;
#include<vector>
#include<list>
#include<map>
#include<set>
#include<string>
#include<assert.h>
namespace bit
{
class string
{
public:
typedef char* iterator;
iterator begin()
{
return _str;
}
iterator end()
{
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);
}
// s1.swap(s2)
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;
_str = new char[s._capacity + 1];
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
// 移动构造 -- 右值(将亡值)
string(string&& s)
{
cout << "string(string&& s) -- 移动拷贝" << endl;
swap(s);
}
// 拷贝赋值
// s2 = tmp
string& operator=(const string& s)
{
cout << "string& operator=(const string& s) -- 深拷贝" << endl;
string tmp(s);
swap(tmp);
return *this;
}
// 移动赋值
string& operator=(string&& s)
{
cout << "string& operator=(string&& s) -- 移动拷贝" << endl;
swap(s);
return *this;
}
~string()
{
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];
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)
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
const char* c_str() const
{
return _str;
}
private:
char* _str = nullptr;
size_t _size = 0;
size_t _capacity = 0; // 不包含最后做标识的\0
};
bit::string to_string(int value)
{
bool flag = true;
if (value < 0)
{
flag = false;
value = 0 - value;
}
bit::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()
{
std::list<bit::string> lt1;
bit::string s1("xxxx");
lt1.push_back(s1);
lt1.push_back(move(s1));
cout << "=============================================" << endl;
bit::string s2("xxxx");
lt1.emplace_back(s2);
lt1.emplace_back(move(s2));
cout << "=============================================" << endl;
lt1.push_back("xxxx");
lt1.emplace_back("xxxx");
cout << "=============================================" << endl;
std::list<pair<bit::string, bit::string>> lt2;
pair<bit::string, bit::string> kv1("xxxx", "yyyy");
lt2.push_back(kv1);
lt2.push_back(move(kv1));
cout << "=============================================" << endl;
pair<bit::string, bit::string> kv2("xxxx", "yyyy");
lt2.emplace_back(kv2);
lt2.emplace_back(move(kv2));
cout << "=============================================" << endl;
lt2.emplace_back("xxxx", "yyyy");
cout << "=============================================" << endl;
return 0;
}
验证浅拷贝
class Date
{
public:
Date(int year, int month, int day)
:_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 = 1;
int _month = 1;
int _day = 1;
};
int main()
{
std::list<Date> lt1;
lt1.push_back({ 2024,3,30 });
// 不支持
//lt1.emplace_back({ 2024,3,30 });
// 推荐
lt1.emplace_back(2024, 3, 30);
cout << endl;
Date d1(2023, 1, 1);
lt1.push_back(d1);
lt1.emplace_back(d1);
cout << endl;
lt1.push_back(Date(2023, 1, 1));
lt1.emplace_back(Date(2023, 1, 1));
return 0;
}
结论:
1、对于插入单个对象而言,emplace_back和push_back没有区别
2、对于需要深拷贝的类对象,emplace_back可以减少一次移动构造
3、对于需要浅拷贝的类对象,emplace_back可以减少一次拷贝构造
4、emplace_back不支持使用{}传参
5、emplace_back可以用于尾插不支持拷贝构造的自定义类(由3推出)
~over~