您的位置:首页 > 编程语言 > C语言/C++

个人翻译Beyond the C++ Standard Library

2006-01-02 17:03 471 查看
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方法.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐