将资源锁定对象作为参数传递

时间:2011-10-30 01:24:26

标签: c++ reference raii

将对象作为参数传递的好方法是什么?在我的代码中,我一直在使用引用而不是指针,如果可能的话,我想坚持这种方法。但是,有一种情况不能正常工作:

class Resource {
    Resource(...) { //expensive resource initialization }
    ~Resource() { //destroy resource }
};

class User {
    User(const Resource & res) : res_(res) { //initialize user }
private:
    Resource res_;
}

void Init() {
    Resource res(...);
    User user = new User(res);
} //res is destroyed - along with its associated resource
//user can no longer use the resource

在这种情况下是否应该使用最佳实践?我想要实现的结果是在Resource对象被销毁时自动销毁User,但不依赖于User对象来创建Resource本身。我的感觉是我应该重载Resource的复制构造函数。还有其他想法吗?

1 个答案:

答案 0 :(得分:1)

你最好的镜头是在shared_ptr<Resource>课程中持有User成员。但这需要在免费(堆)内存上生成资源。

class User {
  User(Resource * res) : res_ptr(res) { //initialize user }
private:
  shared_ptr<Resource> res_ptr;
}

void Init() {
  Resource * res = new Resource(...);
  User user = new User(res);
} 

第二种方法是为Resource提供假拷贝构造函数和交换机制。

class Resource
{
private:
  explicit Resource(const Resource &); // disabled to avoid heavy copy.
  const Resource & operator = (const Resource & );
  int * int_res;
public:
  Resource() : int_res(new int(100)) { } 

  ~Resource()
  {
    if(int_res != NULL)
      delete int_res;
  }

  Resource(Resource & other) : int_res(NULL) 
  {
    this->swap(other);
  }

  void swap(Resource & other)
  {
    using std::swap;
    swap(int_res, other.int_res);
  }
};

class User
{
private:
  Resource resource;
  User();
  User(const User &);
  const User & operator = (const User &);
public:
  User(Resource & res) : resource(res) {  }
  ~User() { }
};

但这很危险且容易出错,因为在创建用户之后,您将资源置于僵尸状态。对于这段代码,对int_res指针(对NULL进行分配)的所有访问都将为您提供访问冲突错误。

我能解释的最后一种方法是使用C ++ 0x功能“移动语义”。

class Resource
{
// ...
// ...
  // Replace with Resource(Resource & other)
  Resource(Resource && other) : int_res(NULL) //Move constructor
  {
    this->swap(other);
  }

  const Resource & operator = (Resource && other) // Move assignment operator
  {
      this->swap(other);
      return *this;
  }

  void swap(Resource & other)
  {
    using std::swap;
    swap(int_res, other.int_res);
  }
};

class User
{
private:
  Resource resource;
  User();
  User(const User &);
  const User & operator = (const User &);
public:
  User(Resource && res) : resource(std::move(res)) {  }
  ~User() { }
};

void Init() {
    Resource res(...);
    User user = new User(std::move(res));
}

这种方法有些安全。您不必处理指针,如果忘记编写std::move,您将收到编译器错误,指出“您无法将Resource实例绑定到Resource&&”或“复制construtor” Resource被禁用。“

&&强调对象是暂时的,并且会消失。因此,这种方法更适用于由函数生成和返回Resource的场景。如果我是你,我会将构造函数设为私有,并使用友元函数生成它。

Resource GetResource(....)
{
  Resource res(...);
  return res;
}

void Init() 
{     
  User user = new User( GetResource(...) );
}

这段代码完全符合“移动语义”。如果能够编写C ++ 0x代码,则必须学习它。 Thisthis是两个很好的视频。