个人翻译Beyond the C++ Standard Library

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方法.

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页