1、新的类功能
1.1 默认成员函数
原来C++类中,有6个默认成员函数:
构造函数、析构函数、拷贝构造函数、赋值运算符重载、取地址重载、const取地址重载
C++11增加了两个:移动构造函数、移动赋值运算符重载
自己实现这两个函数在上一篇文章中已经写过,现在来了解一下编译器默认生成的这两个函数是怎么样的,以及在上面条件之下编译器会自动生成
1. 如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个。那么编译器会自动生成一个默认移动构造。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝(值拷贝),自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
2. 如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。(默认移动赋值跟上面移动构造完全类似)
3. 如果你提供了移动构造或者移动赋值,编译器不会自动提供拷贝构造和拷贝赋值。
为了演示,需要使用自己实现的string
string.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace cxf
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
//默认成员函数
string(const char* str = "");
~string();
string(const string& s);
string(string&& s);
string& operator=(const string& s);//传统写法的赋值运算符重载
//string& operator=(string s);//现代写法的赋值运算符重载
string& operator=(string&& s);
//迭代器
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
//插入删除函数
void reserve(size_t n);
void resize(size_t n, char ch = '\0');
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char* str);
string& erase(size_t pos, size_t len = npos);
//relational operators
bool operator<(const string& s) const;
bool operator==(const string& s) const;
bool operator<=(const string& s) const;
bool operator>(const string& s) const;
bool operator>=(const string& s) const;
bool operator!=(const string& s) const;
//其他函数
void swap(string& s);
const char* c_str() const;
int size() const;
int capacity() const;
char& operator[](size_t i);
const char& operator[](size_t i) const;
size_t find(char ch, size_t pos = 0) const;
size_t find(const char* str, size_t pos = 0) const;
void clear();
private:
char* _str = nullptr;
int _size;
int _capacity;
const static size_t npos;
};
//输入输出函数
std::ostream& operator<<(std::ostream& out, const string& s);
std::istream& operator>>(std::istream& in, string& s);
std::istream& getline(std::istream& in, string& s);
// 将整型转为string
string to_string(int value);
}
string.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
namespace cxf
{
const size_t string::npos = -1;
void string::swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
string::string(const char* str)
:_size(strlen(str))
{
cout << "构造函数" << endl;
_str = new char[_size + 1];
strcpy(_str, str);
_capacity = _size;
}
string::~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//传统写法的拷贝构造
string::string(const string& s)
{
cout << "拷贝构造" << endl;
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
//现代写法的拷贝构造
//string::string(const string& s)
// :_str(nullptr)// 若_str为随机值析构时可能会报错
//{
// string tmp(s._str);
// swap(tmp);
//}
//移动拷贝
string::string(string&& s)
{
cout << "移动构造" << endl;
swap(s);
}
//传统写法的赋值运算符重载
string& string::operator=(const string& s)
{
cout << "赋值运算符重载" << endl;
delete[] _str;
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
return *this;
}
//现代写法的赋值运算符重载
/*string& string::operator=(string s)
{
swap(s);
return *this;
}*/
// 移动赋值
string& string::operator=(string&& s)
{
cout << "移动赋值运算符重载" << endl;
swap(s);
return *this;
}
const char* string::c_str() const
{
return _str;
}
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
string::const_iterator string::begin() const
{
return _str;
}
string::const_iterator string::end() const
{
return _str + _size;
}
int string::size() const
{
return _size;
}
int string::capacity() const
{
return _capacity;
}
char& string::operator[](size_t i)
{
assert(i < _size);
return _str[i];
}
const char& string::operator[](size_t i) const
{
assert(i < _size);
return _str[i];
}
void string::reserve(size_t n)
{
assert(n > _capacity);
char* tmp = new char[n + 1];
strcpy(tmp, _str);
_capacity = n;
delete[] _str;
_str = tmp;
}
void string::resize(size_t n, char ch)
{
if (n < _size)
{
_str[n] = '\0';
}
else
{
if (n > _capacity)
{
reserve(n);
}
for (int i = _size; i < n; i++)
{
_str[i] = ch;
}
_str[n] = '\0';
}
_size = n;
}
void string::push_back(char ch)
{
if (_size == _capacity)
{
int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
void string::append(const char* str)
{
int len = strlen(str);
if (_capacity < _size + len)
{
reserve(_size + len + 1);
}
strcpy(_str + _size, str);
_size += len;
_capacity = _size;
}
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
string& string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
int i = _size;
while (i >= (int)pos)
{
_str[i + 1] = _str[i];
i--;
}
_str[pos] = ch;
_size++;
return *this;
}
string& string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
int len = strlen(str);
if (_capacity < _size + len)
{
reserve(_size + len + 1);
}
int i = _size;
while (i >= (int)pos)
{
_str[i + len] = _str[i];
i--;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
string& string::erase(size_t pos, size_t len)
{
assert(pos <= _size);
if (len >= _size - pos)
{
_str[pos] = '\0';
}
else
{
int i = pos + len;
while (i <= _size)
{
_str[i - pos] = _str[i];
i++;
}
}
_size -= len;
return *this;
}
size_t string::find(char ch, size_t pos) const
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t string::find(const char* str, size_t pos) const
{
char* p = strstr(_str, str);
if (p == nullptr) return npos;
else return p - _str;
}
bool string::operator<(const string& s) const
{
int ret = strcmp(_str, s._str);
return ret < 0;
}
bool string::operator==(const string& s) const
{
int ret = strcmp(_str, s._str);
return ret == 0;
}
bool string::operator<=(const string& s) const
{
return *this < s || *this == s;
}
bool string::operator>(const string& s) const
{
return !(*this <= s);
}
bool string::operator>=(const string& s) const
{
return !(*this < s);
}
bool string::operator!=(const string& s) const
{
return !(*this == s);
}
void string::clear()
{
_str[0] = '\0';
_size = 0;
}
std::ostream& operator<<(std::ostream& out, const string& s)
{
for (size_t i = 0; i < s.size(); i++)
out << s[i];
return out;
}
//std::istream& operator>>(std::istream& in, string& s)
//{
// s.clear();
// while (1)
// {
// char ch;
// ch = in.get();
// if (ch == ' ' || ch == '\n')
// break;
// s += ch;//这样子容易造成频繁的扩容
// }
//}
std::istream& operator>>(std::istream& in, string& s)
{
s.clear();//需要先将string原先的内容清空
char buff[128];
int i = 0;
while (1)
{
char ch;
ch = in.get();
if (ch == ' ' || ch == '\n')
break;
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
i = 0;
s += buff;
}
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
std::istream& getline(std::istream& in, string& s)
{
s.clear();
char buff[128];
int i = 0;
while (1)
{
char ch;
ch = in.get();
if (ch == '\n')
break;
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
i = 0;
s += buff;
}
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
cxf::string to_string(int value)
{
bool flag = true;
if (value < 0)
{
flag = false;
value = 0 - value;
}
cxf::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;
}
}
class Person
{
public:
Person(const char* name = "77", int age = 0)
:_name(name)
, _age(age)
{}
// 生成移动构造和移动赋值运算符重载
private:
cxf::string _name;
int _age;
};
int main()
{
Person s1;
Person s2 = s1;
Person s3 = std::move(s1);
Person s4;
s4 = std::move(s2);
return 0;
}
结果是,s1调用构造函数,s2调用拷贝构造,s3调用移动拷贝(注意,s3调用完移动拷贝后,s1就变成了原先s3的内容),s4先调用构造函数,再调用移动赋值运算符重载
当我们自己写了析构函数后,就不会生成默认的移动构造和移动赋值运算符重载
class Person
{
public:
Person(const char* name = "77", int age = 0)
:_name(name)
, _age(age)
{}
~Person()
{
}
private:
cxf::string _name;
int _age;
};
int main()
{
Person s1;
Person s2 = s1;
Person s3 = std::move(s1);
Person s4;
s4 = std::move(s2);
return 0;
}
结果是
实际上,需要显示写析构,说明有资源需要释放,也就说明需要写拷贝构造、移动构造、赋值运算符重载、移动赋值运算符重载
自动生成的移动构造,对于Date这样的类是没有什么意义的,和拷贝构造的功能是一样的
但是像上面的Person类就是有意义的,因为Person类是右值时,内部的string也是右值,string就能走移动构造,提高效率
1.2 类成员变量初始化
C++11允许在类定义时给成员变量初始缺省值,默认生成构造函数会使用这些缺省值初始化(这里的初始化指是在初始化列表初始化)
1.3 default
强制生成默认函数的关键字default。C++11可以让你更好的控制要使用的默认函数。假设你要使用某个默认的函数,但是因为一些原因这个函数没有默认生成。比如:我们提供了析构函数,就不会生成移动构造了,那么我们可以使用default关键字显示指定移动构造生成。
class Person
{
public:
Person(const char* name = "77", int age = 0)
:_name(name)
, _age(age)
{}
Person(const Person& p) = default;
Person(Person&& p) = default;
Person& operator=(const Person& p) = default;
Person& operator=(Person&& p) = default;
~Person()
{}
private:
cxf::string _name;
int _age;
};
int main()
{
Person s1;
Person s2 = s1;
Person s3 = std::move(s1);
Person s4;
s4 = std::move(s2);
return 0;
}
这里写了1个就需要写另外3个,否则会互相影响导致程序无法运行
1.4 delete
禁止生成默认函数的关键字delete。如果能想要限制某些默认函数的生成,在C++98中,是该函数设置成private,并且只声明补丁已,这样只要其他人想要调用就会报错。在C++11中更简单,只需在该函数声明加上=delete即可,该语法指示编译器不生成对应函数的默认版本,称=delete修饰的函数为删除函数。
C++98若不想要一个默认成员函数,是只声明不实现,并将声明放在private中,因为若不放在private中,在类外面是可以实现的
class Person
{
public:
Person(const char* name = "77", int age = 0)
:_name(name)
, _age(age)
{}
private:
Person(const Person& p);
Person(Person&& p);
Person& operator=(const Person& p);
Person& operator=(Person&& p);
private:
cxf::string _name;
int _age;
};
C++11后,直接使用delete
class Person
{
public:
Person(const char* name = "77", int age = 0)
:_name(name)
, _age(age)
{}
Person(const Person& p) = delete;
Person(Person&& p) = delete;
Person& operator=(const Person& p) = delete;
Person& operator=(Person&& p) = delete;
private:
cxf::string _name;
int _age;
};
应用场景如有些类不想被拷贝,如IO流,有缓冲区,若拷贝会将缓冲区也一并拷贝
1.5 final和override
继承和多态中的final与override关键字。在前面已经介绍过了,这里就不作介绍了。
2、可变参数模板
2.1 可变参数模板的概念
C++11的新特性可变参数模板能够让您创建可以接受可变参数的函数模板和类模板,相比C++98/03,类模版和函数模版中只能含固定数量的模版参数,可变模版参数无疑是一个巨大的改进。在C++11之前其实也有可变参数的概念,比如printf函数就能够接收任意多个参数,但这是函数参数的可变参数,并不是模板的可变参数。
本篇文章只介绍函数模板的可变参数
2.2 可变参数模板的定义
函数的可变参数模板的定义如下:
template <class ...Args>
void ShowList(Args... args)
{}
上面的参数args前面有省略号,所以它就是一个可变模版参数,我们把带省略号的参数称为“参数包”,它里面包含了0到N(N>=0)个模版参数。
Args是一个模板参数包,args是一个函数形参参数包,声明一个参数包Args...args,这个参数包中可以包含0到任意个模板参数。
可以使用sizeof来获取这个函数模板实例化出的函数有几个形参
template <class ...Args>
void ShowList(Args... args)
{
cout << sizeof...(args) << " ";
}
int main()
{
ShowList();
ShowList(1);
ShowList(1,"xxxxxxx");
ShowList(1,"xxxxxxx",2.2);
return 0;
}
结果是
2.3 展开参数包
若想获取参数包args中的每一个参数,也就是打印参数包的内容,很容易想到的方法是
template<class ...Args>
void ShowList(Args... args)
{
//错误示例:
for (int i = 0; i < sizeof...(args); i++)
{
cout << args[i] << " "; //打印参数包中的每个参数
}
cout << endl;
}
这样是不行的。我们无法直接获取参数包args中的每个参数的,只能通过展开参数包的方式来获取参数包中的每个参数,这是使用可变模版参数的一个主要特点,也是最大的难点,即如何展开可变模版参数。由于语法不支持使用args[i]这样方式获取可变参数,所以我们的用一些奇招来一一获取参数包的值。
展开参数包的方法有多种,本篇文章主要介绍两种,递归函数和逗号表达式
2.3.1 递归函数
template<class T, class ...Args>
void Print(T&& x, Args&&... args)
{
if (sizeof...(args) == 0) return;
cout << x << " ";
Print(args...);
}
template<class ...Args>
void ShowList(Args&&... args)
{
Print(args...);
}
将参数包传递给Print,参数包第一个参数就会传递给T,剩下的参数就会传递给args
但是像上面这样实现Print是有错误的,不能使用if来作为递归的结束条件,因为判断是在运行时,而模板实例化是在编译时。结束条件应该写一个无参的Print,当参数包中无参时就会自动匹配到
void Print()
{}
template<class T, class ...Args>
void Print(T&& x, Args&&... args)
{
cout << x << " ";
Print(args...);
}
template<class ...Args>
void ShowList(Args&&... args)
{
Print(args...);
cout << endl;
}
以传3个参数的为例,看看模板是如何实例化的
void Print(double x)
{
cout << x << " ";
Print();
}
void Print(const char* x, double y)
{
cout << x << " ";
Print(y);
}
void Print(int x, const char* y, double z)
{
cout << x << " ";
Print(y, z);
}
void ShowList(int x, const char* y, double z)
{
Print(x, y, z);
}
int main()
{
ShowList(1,"xxxxxxx",2.2);
cout << endl;
return 0;
}
可变参数模板节省了很多个模板
2.3.2 逗号表达式
template<class T>
void PrintArg(T&& t)
{
cout << t << " ";
}
template<class ...Args>
void ShowList(Args&&... args)
{
int arr[] = { (PrintArg(args), 0)... };
cout << endl;
}
这是利用arr[]没有指定大小,所以会去后面推导数组有多大,所以会先执行{}中的内容。用逗号表达式是因为PrintArg没有返回值,而arr是int数组,利用逗号表达式的结果是最后面的数,也就是在arr的相应位置填上0
也可以去掉逗号表达式,此时将PrintArg修改成有返回值即可
template<class T>
int PrintArg(T&& t)
{
cout << t << " ";
return 0;
}
template<class ...Args>
void ShowList(Args&&... args)
{
int arr[] = { PrintArg(args)... };
cout << endl;
}
实例化出来是这样的,以3个参数的为例
int PrintArg(int x)
{
cout << x << " ";
return 0;
}
int PrintArg(char x)
{
cout << x << " ";
return 0;
}
int PrintArg(std::string x)
{
cout << x << " ";
return 0;
}
void ShowList(int x, char y, std::string z)
{
int arr[] = { PrintArg(x),PrintArg(y),PrintArg(z) };
cout << endl;
}
int main()
{
ShowList(1, 'A', "xxxxxxx");
return 0;
}
结果是
也可以将PrintArg和ShowList合二为一
template<class ...Args>
void ShowList(Args&&... args)
{
int arr[] = { (cout << (args) << " ", 0)... };
cout << endl;
}
此时就必须使用逗号表达式了,因为cout返回的是ostream对象,而数组是int数组。注意,不能使用ostream数组,因为ostream数组不允许拷贝
3、emplace_back
在C++11中,大部分容器都增加了empalce类接口,本篇文章以list的emplace_back来介绍
3.1 emplace_back的使用
emplace_back是既可接收左值,也可接收右值(注意这是万能引用,不是右值引用),且参数个数不确定的函数
int main()
{
list<cxf::string> lt;
// 左值
cxf::string s1("1111111");
lt.emplace_back(s1);
// 右值
lt.emplace_back(move(s1));
return 0;
}
结果是
上面的用法是类似于push_back的
int main()
{
list<pair<cxf::string, int>> lt;
pair<cxf::string, int> kv("苹果", 1);
lt.emplace_back(kv);
lt.emplace_back(move(kv));
cout << endl;
// 上面是类似于push_back的写法
// emplace_back是可变参数模板,所以可以直接构造pair的参数包往下传,直接用pair参数包构造pair
lt.emplace_back("苹果", 1); // 此时只有一次构造
return 0;
}
结果是
list<类型> lt;
lt.emplace_back(传类型对应的构造函数的参数)
这样就可以直接构造这个类型的参数包往下传,直接用这个类型的参数包区调用这个类型的构造函数。注意,emplace_back是可变参数模板,不是说一次可以插入多个对象,而是可以根据存储在容器中的数据类型的构造函数来传参
现在我们对自己实现的string增加一个构造函数,其中reserve和成员变量的默认初始值都有所变化
string.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
namespace cxf
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
//默认成员函数
string(const char* str = "");
string(size_t n, char ch = '\0');
~string();
string(const string& s);
string(string&& s);
string& operator=(const string& s);//传统写法的赋值运算符重载
//string& operator=(string s);//现代写法的赋值运算符重载
string& operator=(string&& s);
//迭代器
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;
//插入删除函数
void reserve(size_t n);
void resize(size_t n, char ch = '\0');
void push_back(char ch);
void append(const char* str);
string& operator+=(char ch);
string& operator+=(const char* str);
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char* str);
string& erase(size_t pos, size_t len = npos);
//relational operators
bool operator<(const string& s) const;
bool operator==(const string& s) const;
bool operator<=(const string& s) const;
bool operator>(const string& s) const;
bool operator>=(const string& s) const;
bool operator!=(const string& s) const;
//其他函数
void swap(string& s);
const char* c_str() const;
int size() const;
int capacity() const;
char& operator[](size_t i);
const char& operator[](size_t i) const;
size_t find(char ch, size_t pos = 0) const;
size_t find(const char* str, size_t pos = 0) const;
void clear();
private:
char* _str = nullptr;
int _size = 0;
int _capacity = 0;
const static size_t npos;
};
//输入输出函数
std::ostream& operator<<(std::ostream& out, const string& s);
std::istream& operator>>(std::istream& in, string& s);
std::istream& getline(std::istream& in, string& s);
// 将整型转为string
string to_string(int value);
}
string.cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
namespace cxf
{
const size_t string::npos = -1;
void string::swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
string::string(const char* str)
:_size(strlen(str))
{
cout << "构造函数" << endl;
_str = new char[_size + 1];
strcpy(_str, str);
_capacity = _size;
}
string::string(size_t n, char ch)
{
cout << "string(size_t n, char ch)" << endl;
reserve(n);
for (size_t i = 0; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
string::~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
//传统写法的拷贝构造
string::string(const string& s)
{
cout << "拷贝构造" << endl;
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
}
//现代写法的拷贝构造
//string::string(const string& s)
// :_str(nullptr)// 若_str为随机值析构时可能会报错
//{
// string tmp(s._str);
// swap(tmp);
//}
//移动拷贝
string::string(string&& s)
{
cout << "移动构造" << endl;
swap(s);
}
//传统写法的赋值运算符重载
string& string::operator=(const string& s)
{
cout << "赋值运算符重载" << endl;
delete[] _str;
_size = s._size;
_capacity = s._capacity;
_str = new char[_capacity + 1];
strcpy(_str, s._str);
return *this;
}
//现代写法的赋值运算符重载
/*string& string::operator=(string s)
{
swap(s);
return *this;
}*/
// 移动赋值
string& string::operator=(string&& s)
{
cout << "移动赋值运算符重载" << endl;
swap(s);
return *this;
}
const char* string::c_str() const
{
return _str;
}
string::iterator string::begin()
{
return _str;
}
string::iterator string::end()
{
return _str + _size;
}
string::const_iterator string::begin() const
{
return _str;
}
string::const_iterator string::end() const
{
return _str + _size;
}
int string::size() const
{
return _size;
}
int string::capacity() const
{
return _capacity;
}
char& string::operator[](size_t i)
{
assert(i < _size);
return _str[i];
}
const char& string::operator[](size_t i) const
{
assert(i < _size);
return _str[i];
}
void string::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 string::resize(size_t n, char ch)
{
if (n < _size)
{
_str[n] = '\0';
}
else
{
if (n > _capacity)
{
reserve(n);
}
for (int i = _size; i < n; i++)
{
_str[i] = ch;
}
_str[n] = '\0';
}
_size = n;
}
void string::push_back(char ch)
{
if (_size == _capacity)
{
int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
void string::append(const char* str)
{
int len = strlen(str);
if (_capacity < _size + len)
{
reserve(_size + len + 1);
}
strcpy(_str + _size, str);
_size += len;
_capacity = _size;
}
string& string::operator+=(char ch)
{
push_back(ch);
return *this;
}
string& string::operator+=(const char* str)
{
append(str);
return *this;
}
string& string::insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size == _capacity)
{
int newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
int i = _size;
while (i >= (int)pos)
{
_str[i + 1] = _str[i];
i--;
}
_str[pos] = ch;
_size++;
return *this;
}
string& string::insert(size_t pos, const char* str)
{
assert(pos <= _size);
int len = strlen(str);
if (_capacity < _size + len)
{
reserve(_size + len + 1);
}
int i = _size;
while (i >= (int)pos)
{
_str[i + len] = _str[i];
i--;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
string& string::erase(size_t pos, size_t len)
{
assert(pos <= _size);
if (len >= _size - pos)
{
_str[pos] = '\0';
}
else
{
int i = pos + len;
while (i <= _size)
{
_str[i - pos] = _str[i];
i++;
}
}
_size -= len;
return *this;
}
size_t string::find(char ch, size_t pos) const
{
assert(pos < _size);
for (size_t i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t string::find(const char* str, size_t pos) const
{
char* p = strstr(_str, str);
if (p == nullptr) return npos;
else return p - _str;
}
bool string::operator<(const string& s) const
{
int ret = strcmp(_str, s._str);
return ret < 0;
}
bool string::operator==(const string& s) const
{
int ret = strcmp(_str, s._str);
return ret == 0;
}
bool string::operator<=(const string& s) const
{
return *this < s || *this == s;
}
bool string::operator>(const string& s) const
{
return !(*this <= s);
}
bool string::operator>=(const string& s) const
{
return !(*this < s);
}
bool string::operator!=(const string& s) const
{
return !(*this == s);
}
void string::clear()
{
_str[0] = '\0';
_size = 0;
}
std::ostream& operator<<(std::ostream& out, const string& s)
{
for (size_t i = 0; i < s.size(); i++)
out << s[i];
return out;
}
//std::istream& operator>>(std::istream& in, string& s)
//{
// s.clear();
// while (1)
// {
// char ch;
// ch = in.get();
// if (ch == ' ' || ch == '\n')
// break;
// s += ch;//这样子容易造成频繁的扩容
// }
//}
std::istream& operator>>(std::istream& in, string& s)
{
s.clear();//需要先将string原先的内容清空
char buff[128];
int i = 0;
while (1)
{
char ch;
ch = in.get();
if (ch == ' ' || ch == '\n')
break;
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
i = 0;
s += buff;
}
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
std::istream& getline(std::istream& in, string& s)
{
s.clear();
char buff[128];
int i = 0;
while (1)
{
char ch;
ch = in.get();
if (ch == '\n')
break;
buff[i++] = ch;
if (i == 127)
{
buff[i] = '\0';
i = 0;
s += buff;
}
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
cxf::string to_string(int value)
{
bool flag = true;
if (value < 0)
{
flag = false;
value = 0 - value;
}
cxf::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()
{
list<cxf::string> lt;
lt.emplace_back("111111111");
lt.emplace_back(10, 'x');
return 0;
}
结果是
像string可以使用上面两种方式构造,emplace_back就会直接把构造string的参数包往下传,直接用string参数包构造string
3.2 emplace_back的模拟实现
我们现在来模拟实现list中的emplace_back
引入我们之前实现的list
#pragma once
namespace cxf
{
template<class T>
struct __List_node//创建一个T类型的链表结点
{
__List_node(const T& data = T())//构造函数
:_data(data)
, _next(nullptr)
, _prev(nullptr)
{}
__List_node(T&& data)//构造函数
:_data(move(data))
, _next(nullptr)
, _prev(nullptr)
{}
__List_node<T>* _next;
__List_node<T>* _prev;
T _data;
};
template<class T, class Ref, class Ptr>
struct __List_iterator//封装链表的迭代器
{
typedef __List_node<T> Node;
typedef __List_iterator<T, Ref, Ptr> Self;
Node* _node;//成员变量
__List_iterator(Node* node)//构造函数。将迭代器中的结点初始化成传过来的结点
:_node(node)
{}
// *it
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
// ++it
Self& operator++()
{
_node = _node->_next;
return *this;
}
// it++
Self operator++(int)
{
Self tmp(*this);//调用默认的拷贝构造,因为是指针类型所以直接用默认的
//_node = _node->_next;
++(*this);
return tmp;
}
// --it
Self& operator--()
{
_node = _node->_prev;
return *this;
}
// it--
Self operator--(int)
{
Self tmp(*this);
//_node = _node->_prev;
--(*this);
return tmp;
}
// it != end()
bool operator!=(const Self& it)
{
return _node != it._node;
}
};
template<class T>
class List//真正的链表
{
public:
typedef __List_node<T> Node;//将链表结点的名称重命名为Node
typedef __List_iterator<T, T&, T*> iterator;
typedef __List_iterator<T, const T&, const T*> const_iterator;
//带头双向循环链表
List()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
List(std::initializer_list<T> il)
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
for (auto e : il)
push_back(e);
}
~List()
{
clear();
delete _head;
_head = nullptr;
}
List(const List<T>& lt)
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
//const_iterator it = lt.begin();//这里迭代器不需要指定是那个类域,因为就是在这个类中使用
//while (it != lt.end())
//{
// push_back(*it);
// ++it;
//}
for (const auto& e : lt)//这里与上面用迭代器一样,因为最终也会被替换成迭代器
push_back(e);
}
/*List<T>& operator=(const List<T>& lt)
{
if (this != <)
{
clear();
for (const auto& e : lt)
push_back(e);
}
return *this;
}*/
List<T>& operator=(List<T> lt)
{
swap(_head, lt._head);//原来的空间给这个临时变量,因为这个临时变量是自定义类型,出了作用域后会自动调用析构函数
return *this;
}
void clear()//clear是清除除了头节点意外的所以结点
{
iterator it = begin();
while (it != end())
{
erase(it++);
}
}
void push_back(const T& x)//一定要用引用,因为T不一定是内置类型
{
/*Node* tail = _head->_prev;
Node* newnode = new Node(x);
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;*/
insert(end(),x);
}
void push_back(T&& x)
{
insert(end(), move(x));
}
void pop_back()
{
/*Node* tail = _head->_prev;
Node* prev = tail->_prev;
delete tail;
_head->_prev = prev;
prev->_next = _head;*/
erase(--end());
}
void push_front(const T& x)
{
/*Node* first = _head->_next;
Node* newnode = new Node(x);
_head->_next = newnode;
newnode->_prev = _head;
newnode->_next = first;
first->_prev = newnode;*/
insert(begin(), x);
}
void pop_front()
{
/*Node* first = _head->_next;
Node* second = first->_next;
delete first;
_head->_next = second;
second->_prev = _head;*/
erase(begin());
}
void insert(iterator pos, const T& x)
{
Node* newnode = new Node(x);
Node* cur = pos._node;
Node* prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
void insert(iterator pos, T&& x)
{
Node* newnode = new Node(move(x));
Node* cur = pos._node;
Node* prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
void erase(iterator pos)
{
assert(pos != end());//不能删除头节点
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
cur = nullptr;
}
iterator begin()
{
return iterator(_head->_next);//使用这个结点去构造一个迭代器,并将这个迭代器返回
}
iterator end()
{
return iterator(_head);
}
const_iterator begin() const
{
return const_iterator(_head->_next);//使用这个结点去构造一个迭代器,并将这个迭代器返回
}
const_iterator end() const
{
return const_iterator(_head);
}
private:
Node* _head;
};
}
实现emplace_back与push_back类似,就是x变成了一个参数包
template<class... Args>
void emplace_back(Args&&... args)
{
insert(end(), args...);
}
template<class... Args>
void insert(iterator pos, Args&&... args)
{
Node* newnode = new Node(args...);
Node* cur = pos._node;
Node* prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
结点的构造函数
template<class... Args>
__List_node(Args&&... args)//构造函数
: _data(args...)
, _next(nullptr)
, _prev(nullptr)
{}
运行下面代码
int main()
{
cxf::List<cxf::string> lt;
cxf::string s1("7777777");
lt.emplace_back(s1);
lt.emplace_back("111111111");
lt.emplace_back(10, 'x');
return 0;
}
会发现无论是左值还是右值,都是使用深拷贝
因为存在退化,此时就需要用到完美转发了
template<class... Args>
void emplace_back(Args&&... args)
{
insert(end(), std::forward<Args>(args)...);
}
template<class... Args>
void insert(iterator pos, Args&&... args)
{
Node* newnode = new Node(std::forward<Args>(args)...);
Node* cur = pos._node;
Node* prev = cur->_prev;
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
}
template<class... Args>
__List_node(Args&&... args)//构造函数
: _data(std::forward<Args>(args)...)
, _next(nullptr)
, _prev(nullptr)
{}