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

发布于 2024-12-13 00:47:50 字数 677 浏览 0 评论 0原文

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

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

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

What is a good way of passing an object as a parameter? In my code, I have been using references instead of pointers, and I would like to stick to this approach if possible. However, there is one case where this does not work properly:

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

Are there any best practices that should be used in such a scenario? The result I want to achieve is to destroy the Resource automatically when the User object is destroyed, but not rely on the User object to create the Resource itself. My feeling is that I should overload the copy constructor of Resource. Any other ideas?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

上课铃就是安魂曲 2024-12-20 00:47:50

最好的办法是在 User 类中保留一个 shared_ptr 成员。但这需要在空闲(堆)内存上生成资源。

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() { }
};

但这是危险且容易出错的,因为创建用户后,您会将资源置于僵尸状态。对于这段代码,所有对分配给NULLint_res指针的访问都会给你带来访问冲突错误。

我可以解释的最后一种方法是使用 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 实例绑定到 < code>Resource&&”或“Resource 的复制构造函数已禁用。”

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

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

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

这段代码在“移动语义”下表现完美。如果您能够编写 C++0x 代码,则必须学习它。 是两个很好的视频。

Your best shot is holding a shared_ptr<Resource> member in User class. But this requires the resource to be generated on the free (heap) memory.

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);
} 

Second approach is providing a fake copy constructor and a swap mechanism for 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() { }
};

But this is dangerous and error-prone because after creating the user, you leave the resource in a zombie state. For this piece of code, all access to the int_res pointer, which is assgined to NULL, will give you access violation error.

The last approach I can explain is using a C++0x feature "move semantics".

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));
}

This approach is some how safer. You don't have to deal with pointers and if you forget to write std::move, you will get a compiler error saying "you cannot bind a Resource instance to Resource&&" or "copy construtor of Resource is disabled."

&& emphasizes the object is temporary and is going to evaporate. So this approach is more suitable for scenarios where Resource is generated and returned by a function. If I were you I would make the constructor private and generate it with a friend function.

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

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

This piece of code is perfectly performant with "move semantics". You must learn it if you are able to write C++0x code. This and this are two good videos to start with.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文