c++标准库-c++通用工具

本文深入探讨了C++标准库中的关键数据结构和智能指针,包括Pair和Tuple的使用,以及shared_ptr和weak_ptr的高级特性,展示了如何避免循环引用和正确管理资源。

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> pdefaulr构造函数,建立一个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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值