个人翻译Beyond the C++ Standard Library

    技术2022-05-11  124

    The Pimpl Idiom Revisited

    pimpl idiom在上面和scoped_ptr一起出现过,像存储动态分配的pimpl实例一样,如果使用idion的

    类不允许复制.不是所有使用pimpl idiom的类都适用的(scoped_ptr仍能被使用,但是copy

    construction和assignment需要自己动手实现).为了那些类能操作被共享的实现细节,shared_ptr

    被引入了进来.当pimpl的所有权被传给shared_ptr,copying和assignment operators不带来任何

    消耗.当scoped_ptr处理pimpl类的生命期时你可以回调它,复制外部类是不被允许的,因为

    scoped_ptrs是不可复制的.这意味着支持copying和assignment这样的类,copy constructor和

    assignment operator必须自己定义.当scoped_ptr处理pimpl类的生命期时,也许不需要一个用户

    自定义的copy constructor.注意pimpl实例将被类的所有对象们共享,所以如果有这样一种仅应用

    于类的一个实例的情况,一个手写的copy constructor仍然是必须的.答案和scoped_ptr的解决方

    案很相似;用shared_ptr替换.

    shared_ptr and Standard Library Containers

    直接在容器中存储对象有时候是很麻烦的.存储对象的值意味着用户获得的是容器元素的副本,这

    或许是一个类型的性能问题并带来很大的开销.此外,一些容器,特别是std::vector,元素复制发生

    在当你添加更多元素大小改变时,进一步增加了性能问题.最后,元素没有多态行为.如果你要在容

    器中存储多态对象并且你不想slice它们,你必须使用指针.如果使用原生指针,维护元素的完整性

    会更加复杂.也就是说,你必须明确用户是否删除容器元素后仍想引用它们, 不担心多个用户使用

    同相同的元素.使用shared_ptr这些问题可以被灵活的解决.

    下面的例子展示如何在Standard Library container中存储shared_ptr.

    #include "boost/shared_ptr.hpp"#include <vector>#include <iostream>

    class A {public:  virtual void sing()=0;protected:  virtual ~A() {};};

    class B : public A {public:  virtual void sing() {    std::cout << "Do re mi fa so la";  }};

    boost::shared_ptr<A> createA() {  boost::shared_ptr<A> p(new B());  return p;}

    int main() {  typedef std::vector<boost::shared_ptr<A> > container_type;  typedef container_type::iterator iterator;

      container_type container;  for (int i=0;i<10;++i) {    container.push_back(createA());  }

      std::cout << "The choir is gathered: /n";  iterator end=container.end();  for (iterator it=container.begin();it!=end;++it) {    (*it)->sing();  }}

    类A和B,包含一个virtual member function sing.B从A中派生,正如你所见,factory function

    createA返回一个被shared_ptr<A>包装的B的dynamically allocated instance.在main中,一个

    std::vector容器包含了10个shared_ptr<A>元素,最后sing被每个元素调用.我们已经使用过原生

    指针作为元素,对象需要手动delete.在一些例子中,delete是自动的,因为容器中的每个

    shared_ptr的reference count是1只要vector仍存在;当vector被destroy,所有的reference

    count都变0,对象被delete.有趣的是即使A的destructor没有被声明为virtual,shared_ptr仍然能

    正确的调用B的destructor!

    例子中示范了一个强大的技术,它涉及了A的protected destructor.因为createA function返回一

    个shared_ptr<A>,对shared_ptr:: get返回的pointer调用delete是不可能的.这意味着

    shared_ptr中的pointer将可能被检查,为了传递它进入一个期望原生指针的function,它是不可能

    会被偶然delete的,那样将带来很严重后果.所以,shared_ptr是如何被允许来delete对象的呢?因

    为指针的实际类型是B;B的destructor不是protected.这是一个非常有用的额外增加shared_ptrs

    保存对象安全的方法.

    shared_ptr and Other Resources

    有时,你会发现你需要使用shared_ptr同一个需要其他delete方法而非普通的delete方法的类型.shared_ptr通过调用用户的delete方法来支持这样的条件.资源操作,像FILE*,或者使用操作系统

    的特殊处理,典型的通过fclose这样的操作来释放.为了在shared_ptr中使用FILE*,我们定义一个

    负责销毁资源的类.

    class FileCloser {public:   void operator()(FILE* file) {    std::cout << "The FileCloser has been called with a FILE*, "      "which will now be closed./n";    if (file!=0)       fclose(file);  }};

    这是一个函数对象我们将用它来确保fclose被调用当资源应该被释放的时候.这是一个利用

    FileCloser class的例子.

    int main() {  std::cout <<     "shared_ptr example with a custom deallocator./n";   {    FILE* f=fopen("test.txt","r");    if (f==0) {      std::cout << "Unable to open file/n";      throw "Unable to open file";    }

        boost::shared_ptr<FILE>       my_shared_file(f, FileCloser());

        // Position the file pointer    fseek(my_shared_file.get(),42,SEEK_SET);  }  std::cout << "By now, the FILE has been closed!/n";}注意获得资源,我们需要使用不易读的&* idiom,get,或者get_pointer涉及shared_ptr时.(我明确

    反对使用&*.其他俩个选择不够清晰)例子使问题简单甚至我们不需要做其他的相比调用一个单参

    数函数当回收资源时,这确实不需要创建一个用户delete方法.重写的例子如下:

    {  FILE* f=fopen("test.txt","r");  if (f==0) {    std::cout << "Unable to open file/n";    throw file_exception();  }    boost::shared_ptr<FILE> my_shared_file(f,&fclose);

      // Position the file pointer  fseek(&*my_shared_file,42,SEEK_SET); }std::cout << "By now, the FILE* has been closed!/n";

    用户delete方法尤其有用对于对于处理需要释放过程的资源.因为用户delete方法不是shared_ptr

    类型的一部分,客户端不需要知道有关smart pointer拥有的资源的任何信息(当然除了怎样使用它

    ).例如,一个被使用对象的pool,用户delete方法将简单的返回对象到pool,或者,一个singleton对

    象有一个不做任何事delete方法.


    最新回复(0)