简体   繁体   English

防止复制返回的参考

[英]Prevent returned reference from being copied

I have some class which manages some resources. 我有一些班级管理一些资源。 You get get a reference to one of those resources from this managing class. 您将从此管理类中获得对这些资源之一的引用。 Resources are heavy-weight objects, so you normally don't want to copy them. 资源是重量级的对象,因此您通常不想复制它们。 In my case, a base-class for resources is no solution, because my 'resource-manager' is a template class which should work with already defined resource classes of other people. 在我的情况下,资源的基类不是解决方案,因为我的“资源管理器”是模板类,应该与其他人已经定义的资源类一起使用。 I made some simple example to show my problem: 我举了一个简单的例子来说明我的问题:

#include <iostream>

// copyable class
class Copyable {
public:
  Copyable() = default;
  Copyable(const Copyable&) {
      std::cout << "Copyconstructor called" << std::endl;
  }

  Copyable& operator=(const Copyable&) {
      std::cout << "assignment operator called" << std::endl;
      return *this;
  }
};

// non copyable class
class NonCopyable {
public:
  NonCopyable() = default;
private:
  NonCopyable(const NonCopyable&) = delete;
  NonCopyable& operator=(const NonCopyable&) = delete;
};

// some class that can return a reference
template <typename T>
class SomeHolder {
private:
    T some_member;
public:    
    T& getReference() {
        return some_member;
    }
};

int main() {
    SomeHolder<Copyable> holder_1;
    auto copyable_1 = holder_1.getReference();    
    auto &copyable_2 = holder_1.getReference();

    SomeHolder<NonCopyable> holder_2;
    //auto noncopyable_1 = holder_2.getReference(); // line 39
    auto &noncopyable_2 = holder_2.getReference();  // line 40
}

The class SomeHolder returns a reference on the owned object. SomeHolder类返回对拥有的对象的引用。 As you see in line 39, this reference gets copied. 如您在第39行中看到的,此引用被复制。 Normal way to get the reference is done in line 40. But if you miss the & you get a copy, which you normally don't want. 获取引用的正常方法是在第40行中完成的。但是,如果您错过了&,则会得到一份副本,而您通常不希望这样做。 If you uncomment line 39, you get an error because the resource isn't copyable in this case. 如果取消注释第39行,则会出现错误,因为在这种情况下该资源不可复制。 Like i said before, wanting all resources to be not copyable isn't a solution in my case. 就像我之前说的,在我看来,要所有资源都不可复制不是解决方案。 Do you have ideas to other design decisions? 您对其他设计决策有想法吗? I write this class to hold resources for games. 我编写此类来保存游戏资源。 I am writing a small library for recurring tasks. 我正在编写一个小型库来执行重复任务。 So i don't know if your framework works with copyable textures and sounds. 因此,我不知道您的框架是否可复制纹理和声音。 Maybe there is no great solution for my problem, but if you have design suggestions, let me know. 也许对于我的问题没有很好的解决方案,但是如果您有设计建议,请告诉我。

You cannot prevent other code from copying objects that are copyable. 您不能阻止其他代码复制可复制的对象。 So, if you want other code to not be able to copy copyable objects that you hold, then you must not return a reference to them. 因此,如果您希望其他代码无法复制您持有的可复制对象,那么您就不能返回对它们的引用。

Instead, you could return a wrapper object, that delegates its functionality to an instance of the copyable class whose reference is held privately within the wrapper: 相反,您可以返回包装器对象,该对象将其功能委托给可复制类的实例,该类的引用在包装器中私有保存:

template <typename T>
class Wrapper {
    T& t;
public:
    Wrapper(T& t) : t(t) {}
    T* operator->() { return &t; }
};

Wrapper<T> SomeHolder<T>::getSomeMember() {
    return {some_member};
}

Since direct member access operator cannot be overloaded, you will need to change the calling code to use the indirect member access instead. 由于直接成员访问运算符不能重载,因此您将需要更改调用代码以使用间接成员访问。

I also suggest to use a smart pointer, but I prefer to not use reference members. 我还建议使用智能指针,但我更喜欢不使用引用成员。 That way client can keep handles. 这样客户可以保持处理。

template<typename T>
struct ViewPtr {
  ViewPtr() = delete;
  ViewPtr(T* tp) : tp(tp) {}
  T* operator->() { return tp; }
  const T* operator->() const { return tp; }
  private:
  T* tp;
};

Usage: 用法:

template <typename T>
class SomeHolder {
private:
  T some_member;
public:
  ViewPtr<T> getReference() {
    return &some_member;
  }
};

This Viewtr also opts to "propagate const". Viewtr还选择“传播const”。 That is: if you have a const view object (or if it's a member, and you are in a const method) you get a const interface. 那就是:如果您有一个const视图对象(或者它是一个成员,并且您在const方法中),您将获得一个const接口。

If you make your objects copyable there will definitely be no way for your to prevent them from copying it.... 如果您使对象可复制,则绝对不会阻止您复制对象。

If you return a reference to a copyable object, you can't prevent developers using your class from copying them at some point. 如果返回对可复制对象的引用,则无法阻止使用类的开发人员在某个时候复制它们。 If they use auto& , the object won't be copied, but if they use ̀ auto , the object will be copied....and even if they used auto& when they called your getReference() function, you can't prevent them from doing a later copy (when they will call a function or assigning a new variable). 如果他们使用auto& ,对象将不会被复制,但如果他们使用auto ,对象将被复制....即使他们使用auto&当他们叫你的getReference()函数,你不能阻止他们从以后进行复制(当他们将调用一个函数或分配一个新变量时)。

An alternative, as commented by "apple apple", is to return a shared_ptr . 正如“苹果苹果”所评论的,另一种方法是返回shared_ptr Then your ressource manager stores shared pointers to ressources and gives access to them. 然后,您的资源管理器存储指向资源的共享指针,并提供对它们的访问权限。 Then, the caller may use many instances of the shared_ptr , all pointing to the same ressource which is not copied in the end. 然后,调用方可能会使用shared_ptr许多实例,所有实例都指向最后没有复制的同一资源。 If he uses auto he gets a copy of the shared_ptr , if he uses auto& he gets a reference to shared_ptr , but, in both case they point to the same ressource which were not copied. 如果他使用auto他将获得shared_ptr的副本,如果他使用auto&他将获得对shared_ptr的引用,但是,在两种情况下,它们均指向相同的资源,但未被复制。

If the caller really wants to do a copy, he will find a way to do it by using shared_ptr::get() to access the copyable object. 如果调用者确实想复制,他将找到一种方法,方法是使用shared_ptr::get()访问可复制对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM