我有一些管理一些资源的课程。您可以从此管理类中获取对其中一个资源的引用。资源是重量级的对象,因此您通常不想复制它们。就我而言,资源的基类不是解决方案,因为我的资源管理器'是一个模板类,它应该与已定义的其他人的资源类一起使用。我举了一些简单的例子来说明我的问题:
#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 ©able_2 = holder_1.getReference();
SomeHolder<NonCopyable> holder_2;
//auto noncopyable_1 = holder_2.getReference(); // line 39
auto &noncopyable_2 = holder_2.getReference(); // line 40
}
SomeHolder类返回对所拥有对象的引用。如第39行所示,此引用被复制。获得参考的正常方式是在第40行完成的。但如果你错过了&amp;你得到一份你通常不想要的副本。如果取消注释第39行,则会出现错误,因为在这种情况下资源不可复制。 就像我之前说过的那样,想要所有资源都不可复制在我的案例中不是一个解决方案。您对其他设计决策有什么想法吗?我写这个课程来保存游戏资源。我正在为重复的任务编写一个小型库。所以我不知道你的框架是否适用于可复制的纹理和声音。也许我的问题没有很好的解决方案,但如果你有设计建议,请告诉我。
答案 0 :(得分:3)
您无法阻止其他代码复制可复制的对象。因此,如果您希望其他代码无法复制您拥有的可复制对象,则不得返回对它们的引用。
相反,您可以返回一个包装器对象,该对象将其功能委托给可复制类的实例,该实例的引用在包装器中私有保存:
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};
}
由于直接成员访问运算符无法重载,因此您需要更改调用代码以改为使用间接成员访问。
答案 1 :(得分:1)
我还建议使用智能指针,但我更喜欢不使用引用成员。这样客户端可以保留句柄。
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;
};
用法:
template <typename T>
class SomeHolder {
private:
T some_member;
public:
ViewPtr<T> getReference() {
return &some_member;
}
};
这个Viewtr
也选择“传播const”。那就是:如果你有一个const视图对象(或者如果它是一个成员,并且你是一个const方法),你会得到一个const接口。
答案 2 :(得分:0)
如果你的对象是可复制的,你肯定无法阻止它们复制它....
如果返回对可复制对象的引用,则无法阻止使用您的类的开发人员在某些时候复制它们。如果他们使用auto&
,则不会复制该对象,但如果他们使用auto
,则会复制该对象....即使他们在调用时使用了auto&
您的getReference()
函数,您无法阻止它们执行以后的复制(当它们调用函数或分配新变量时)。
“苹果苹果”评论的替代方案是返回shared_ptr
。然后,您的资源管理器存储指向资源的共享指针并提供对它们的访问权限。然后,调用者可以使用shared_ptr
的许多实例,所有实例都指向最终未复制的相同资源。如果他使用auto
,他会获得shared_ptr
的副本,如果他使用auto&
,则会获得对shared_ptr
的引用,但在这两种情况下,他们都指向同一个资源没有被复制。
如果调用者真的想要复制,他会找到一种方法来使用shared_ptr::get()
来访问可复制对象。