Pair 和 Tuple
c++11中 Tuple Class被重新实现,有了template可以接受不定实参的能力。可以接受任意大小的异质集合。
Class Pair仍旧为两个元素服务,用来组合一个双元素的Tuple。
Pair
其可以将两个value视为一个单元。尤其容器map, multimap, unordered_map, unorder_multimap
就是使用pair管理其以key/value pair存在的元素。任何函数如果需要返回两个value,也需要用到pair,列如minmax()
- 元素访问
由struct定义,其成员都是public
namespace std{
template <typename T1, typename T2>
struct pair {
//membber
T1 first;
T2 second;
}
}
- pair的操作函数
操作函数 | 影响 |
---|---|
pair<T1,T2> p | defaulr构造函数,建立一个pair,元素类型分别为T1,T2,各自以其default构造函数初始化 |
pair<T1,T2> p(vall,vall) | 建一个pair,元素类型分别为T1,T2,以vall和vall为初始值 |
pair<T1,T2> p(rev1,rev2) | 建一个pair,元素类型分别为T1,T2,以rev1和rev2进行move式初始化(move initialized) |
pair<T1,T2> p(piecewise_construct,t1,t2) | 建立一个pair,元素分别为tuple T1和T2,以tuple t1和t2为初始值 |
pair<T1,T2> p(p2) | Copy构造函数,建立p成为p2的拷贝 |
pair<T1,T2> p(rv) | Move构造函数,将rv的内容移动至p(允许隐式类型转换) |
p=p2 | 将p2赋值给p(始自c++11,允许隐式类型转换) |
p =rv | 将rv的值move assign给p(始自c++11,允许隐式类型转换) |
p.first | 直接访问成员一 |
p.second | 直接访问成员二 |
get<0> (p) | 等价于p.first(始自c++11) |
get<1> (p) | 等价于p.second(始自c++11) |
p1 ==p2 | 返回p1和p2是否相等 |
p1!=p2 | 返回是否p1不等于p2 |
p1<p2 | 返回是否p1小于p2(比较first,如果相等则比较second) |
p1>p2 | 返回是否p1大于p2 |
p1 <=p2 | |
p1 >= p2 | |
p1.swap(p2) | 互换p1和p2的数据(始自c++11) |
swap(p1,p2) | 同上,全局函数(始自c++11) |
make_pair(val1, val2) | 返回一个pair,带有val1和val2的类型和数据 |
举个例子,实现一个泛型函数模板(generic function template),以来将一个value pair写入一个stream:
template<typename T1, typename T2>
std::ostream &operator<<(std::ostream &strm, const std::pair<T1, T2> &p)
{
return strm << "[" << p.first << "," << p.second << "]";
}
cout << make_pair(1,1.23) << endl;
//输出[1,1.23]
typedef std::pair<int,float > IntFloatPair;
IntFloatPair p(2, 1.234);
typedef std::pair<int, float> IntFloatPair;
IntFloatPair p(2, 1.234);
std::get<0>(p);//2
std::get<1>(p);//1.234
std::tuple_size<IntFloatPair>::value; //获得元素的个数
std::tuple_element<0, IntFloatPair>::type; //获得第一个元素的类型
#include <iostream>
#include <utility>
#include <tuple>
using namespace std;
class Foo {
public:
Foo (tuple<int, float>) {
cout << "Foo::Foo(tuple)" << endl;
}
template <typename... Args>
Foo (Args... args) {
cout << "Foo::Foo(args...)" << endl;
}
};
int main()
{
// create tuple t:
tuple<int,float> t(1,2.22);
// pass the tuple as a whole to the constructor of Foo:
pair<int,Foo> p1 (42, t);
// pass the elements of the tuple to the constructor of Foo:
pair<int,Foo> p2 (piecewise_construct, make_tuple(42), t);
}
Tuple
c++11使用variadic tempalte,使的template得以接受任何数量的template实参
#include <tuple>
#include <iostream>
#include <complex>
#include <string>
using namespace std;
int main()
{
// create a four-element tuple
// - elements are initialized with default value (0 for fundamental types)
tuple<string,int,int,complex<double>> t;
// create and initialize a tuple explicitly
tuple<int,float,string> t1(41,6.3,"nico");
// "iterate" over elements:
cout << get<0>(t1) << " ";
cout << get<1>(t1) << " ";
cout << get<2>(t1) << " ";
cout << endl;
// create tuple with make_tuple()
// - auto declares t2 with type of right-hand side
// - thus, type of t2 is tuple
auto t2 = make_tuple(22,44,"nico");//"nico"为const char*
// assign second value in t2 to t1
get<1>(t1) = get<1>(t2);
// comparison and assignment
// - including type conversion from tuple<int,int,const char*> auto t2 = make_tuple(22,44,"nico")
// to tuple<int,float,string>
if (t1 < t2) { // compares value for value,字典比较序
t1 = t2; // OK, assigns value for value
}
}
使用模板超编程,实现在编译器递归迭代tuple的所有元素。
#include <tuple>
#include <iostream>
// helper: print elements with index IDX and higher of tuple t having MAX elements
template <int IDX, int MAX, typename... Args>
struct PRINT_TUPLE {
static void print (std::ostream& strm, const std::tuple<Args...>& t) {
strm << std::get<IDX>(t) << (IDX+1==MAX ? "" : ",");
PRINT_TUPLE<IDX+1,MAX,Args...>::print(strm,t);
}
};
// partial specialization to end the recursion
template <int MAX, typename... Args>
struct PRINT_TUPLE<MAX,MAX,Args...> {
static void print (std::ostream& strm, const std::tuple<Args...>& t) {
}
};
// output operator for tuples,结束迭代的特例化函数
template <typename... Args>
std::ostream& operator << (std::ostream& strm,
const std::tuple<Args...>& t)
{
strm << "[";
PRINT_TUPLE<0,sizeof...(Args),Args...>::print(strm,t);
return strm << "]";
}
智能指针
Class share_ptr
“当对象再也不被使用的时候就被清理”
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
int main()
{
// two shared pointers representing two persons by their name
shared_ptr<string> pNico(new string("nico"));
shared_ptr<string> pJutta(new string("jutta"));
shared_ptr<string> pJuttb{new string("jutta")};//新式初始化
shared_ptr<string> pJuttc = make_shared<string>("jutta");//快速,安全,一次分配过程
shared_ptr<string> pNico4;
//pNico4 = new string("ncio"); ERROR
pNico4.reset(new string("ncio"));
//shared_ptr<string> pJuttb = new string("jutta");//ERROR
// capitalize person names
(*pNico)[0] = 'N';
pJutta->replace(0,1,"J");
// put them multiple times in a container
vector<shared_ptr<string>> whoMadeCoffee;
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pNico);
whoMadeCoffee.push_back(pJutta);
whoMadeCoffee.push_back(pNico);
// print all elements
for (auto ptr : whoMadeCoffee) {
cout << *ptr << " ";
}
cout << endl;
// overwrite a name again
*pNico = "Nicolai";
// print all elements again
for (auto ptr : whoMadeCoffee) {
cout << *ptr << " ";
}
cout << endl;
pNico = nullptr; //pNico dose not refer to the string any longer
whoMadeCoffee.resize(2);//all copies of the string in pNico are destoryed
// print some internal data
cout << "use_count: " << whoMadeCoffee[0].use_count() << endl;
}
- 自定义删除器,针对array
share_ptr 提供的default deleter调用的是delete,不是delete[].这个意味着只有当shared pointer 拥有 由new建立起来的单一对象,default deleter才能正常适用,如下需要传入自定义删除器
std::shared_ptr<int> p(new int[10], [](int *p)
{ delete[] p; });
//或unique_ptr的辅助函数作为deleter,其内部调用delete [];
std::shared_ptr<int> p(new int[10], std::default_delete<int[]>());
//unique_ptr针对array做了偏特化版本,提供operator[]取代 operato* 和operator->
std::unique_ptr<int[]> p3(new int[10]); //OK
std::unique_ptr<int, void (*)(int *)> p2(new int[10], [](int *p)
{
cout << "deete p array" << endl;
delete[] p;
});
std::shared_ptr<int[]> p4(new int[10]);//ERROR
- 自定义删除器,针对自定义删除策略
清理工作除了删除内存,还需要其他清理行为,如下例子所示。
//假设我们想确保“指向某临时文件”之最末一个reference被销毁时,该文件随机被移除。可以这么做。
#include <string>
#include <fstream> // for ofstream
#include <memory> // for shared_ptr
#include <cstdio> // for remove()
#include <iostream>
class FileDeleter
{
private:
std::string filename;
public:
FileDeleter (const std::string& fn)
: filename(fn) {
}
void operator () (std::ofstream* fp) {
std::cout << "delete fp" << std::endl;
delete fp; // close file
std::remove(filename.c_str()); // delete file
}
};
int main()
{
// create and open temporary file:
std::shared_ptr<std::ofstream> fp(new std::ofstream("tmpfile.txt")/*新建一个输出文件由share_ptr管理*/,
FileDeleter("tmpfile.txt"));
//...
}
一般说来,建议使用自定义类 的构造函数执行初始化工作,其析构函数执行清理工作。然后使用share_ptr管理以new 建立的class x对象。对外可以定义一个更加直观的接口。
Class weak_ptr
“明确想共享,但是不想拥有”
下面看一下weak_ptr的应用场景变迁:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
class Person {
public:
string name;
shared_ptr<Person> mother;
shared_ptr<Person> father;
vector<shared_ptr<Person>> kids;
Person (const string& n,
shared_ptr<Person> m = nullptr,
shared_ptr<Person> f = nullptr)
: name(n), mother(m), father(f) {
}
~Person() {
cout << "delete " << name << endl;
}
};
shared_ptr<Person> initFamily (const string& name)
{
shared_ptr<Person> mom(new Person(name+"'s mom"));
shared_ptr<Person> dad(new Person(name+"'s dad"));
shared_ptr<Person> kid(new Person(name,mom,dad));
mom->kids.push_back(kid);//循环引用
dad->kids.push_back(kid);//循环引用
return kid;
}
int main()
{
shared_ptr<Person> p = initFamily("nico");
cout << "nico's family exists" << endl;
cout << "- nico is shared " << p.use_count() << " times" << endl;
cout << "- name of 1st kid of nico's mom: "
<< p->mother->kids[0]->name << endl;
p = initFamily("jim");
cout << "jim's family exists" << endl;
}
输出:
nico's family exists
- nico is shared 3 times
- name of 1st kid of nico's mom: nico
jim's family exists
Process finished with exit code 0
借助weak_ptr实现“明确想共享,但是不想拥有”的功能逻辑,从而实现资源安全释放:
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
class Person {
public:
string name;
shared_ptr<Person> mother;
shared_ptr<Person> father;
vector<weak_ptr<Person>> kids; // weak pointer !!!
Person (const string& n,
shared_ptr<Person> m = nullptr,
shared_ptr<Person> f = nullptr)
: name(n), mother(m), father(f) {
}
~Person() {
cout << "delete " << name << endl;
}
};
shared_ptr<Person> initFamily (const string& name)
{
shared_ptr<Person> mom(new Person(name+"'s mom"));
shared_ptr<Person> dad(new Person(name+"'s dad"));
shared_ptr<Person> kid(new Person(name,mom,dad));
mom->kids.push_back(kid);
dad->kids.push_back(kid);
return kid;
}
int main()
{
shared_ptr<Person> p = initFamily("nico");
cout << "nico's family exists" << endl;
cout << "- nico is shared " << p.use_count() << " times" << endl;
cout << "- name of 1st kid of nico's mom: "
<< p->mother->kids[0].lock()->name << endl;//如果此时内含share_ptr不存在,会引起不可知后果
if (auto q = p->mother->kids[0].lock()) {
cout << q->name << endl;
}
if (!p->mother->kids[0].expired()) {
cout << "- name of 1st kid of nico's mom: "
<< p->mother->kids[0].lock()->name << endl;
}
p = initFamily("jim");
cout << "jim's family exists" << endl;
}
输出:
nico's family exists
- nico is shared 1 times
- name of 1st kid of nico's mom: nico
delete nico
delete nico's dad
delete nico's mom
jim's family exists
delete jim
delete jim's dad
delete jim's mom