C++ 防止复制返回的引用
我有一个班级管理一些资源。您可以从这个管理类中获得对其中一个资源的引用。资源是很重的对象,所以您通常不想复制它们。在我的例子中,资源的基类不是解决方案,因为我的“资源管理器”是一个模板类,它应该与其他人已经定义的资源类一起工作。我举了一些简单的例子来说明我的问题:C++ 防止复制返回的引用,c++,c++11,C++,C++11,我有一个班级管理一些资源。您可以从这个管理类中获得对其中一个资源的引用。资源是很重的对象,所以您通常不想复制它们。在我的例子中,资源的基类不是解决方案,因为我的“资源管理器”是一个模板类,它应该与其他人已经定义的资源类一起工作。我举了一些简单的例子来说明我的问题: #include <iostream> // copyable class class Copyable { public: Copyable() = default; Copyable(const Copyab
#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
}
#包括
//可复制类
类可复制{
公众:
Copyable()=默认值;
可复制(常量可复制&){
std::cout如果你让你的对象可复制,你肯定没有办法阻止他们复制它
如果您返回对可复制对象的引用,您无法阻止使用您的类的开发人员在某个时候复制它们。如果他们使用auto&
,则不会复制该对象,但如果他们使用̀auto
,则会复制该对象……即使他们在调用您的getReference()时使用了auto&
)
function,您无法阻止他们稍后进行复制(当他们调用函数或分配新变量时)
另一种选择,正如“苹果”评论的那样,是返回一个共享的\u ptr
。然后,您的资源管理器存储指向资源的共享指针并授予对它们的访问权限。然后,调用者可能会使用共享的\u ptr
的多个实例,所有实例都指向最后未复制的同一个资源。如果他使用自动,他将获得一份共享的\u ptr
,我将如果他使用auto&
,他会得到一个对shared\u ptr
的引用,但在这两种情况下,它们都指向未复制的相同资源
如果调用者真的想复制,他会找到一种方法,使用shared\u ptr::get()
访问可复制对象。如果你让对象可复制,你肯定没有办法阻止他们复制
如果您返回对可复制对象的引用,您无法阻止使用您的类的开发人员在某个时候复制它们。如果他们使用auto&
,则不会复制该对象,但如果他们使用̀auto
,则会复制该对象……即使他们在调用您的getReference()时使用了auto&
)
function,您无法阻止他们稍后进行复制(当他们调用函数或分配新变量时)
另一种选择,正如“苹果”评论的那样,是返回一个共享的\u ptr
。然后,您的资源管理器存储指向资源的共享指针并授予对它们的访问权限。然后,调用者可能会使用共享的\u ptr
的多个实例,所有实例都指向最后未复制的同一个资源。如果他使用自动,他将获得一份共享的\u ptr
,我将如果他使用auto&
,他会得到一个对shared\u ptr
的引用,但在这两种情况下,它们都指向未复制的相同资源
如果调用者真的想复制,他会通过使用shared\u ptr::get()找到复制的方法
访问可复制对象。您不能阻止其他代码复制可复制的对象。因此,如果您希望其他代码无法复制您持有的可复制对象,则不能返回对它们的引用
相反,您可以返回一个包装器对象,该对象将其功能委托给可复制类的一个实例,该类的引用在包装器中私自保存:
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};
}
模板
类包装器{
T&T;
公众:
包装器(T&T):T(T){}
T*运算符->(){return&T;}
};
包装器SomeHolder::getSomeMember(){
返回{some_member};
}
由于无法重载直接成员访问运算符,您将需要更改调用代码以使用间接成员访问。您无法阻止其他代码复制可复制的对象。因此,如果您希望其他代码无法复制您持有的可复制对象,则您不能返回对这些对象的引用
相反,您可以返回一个包装器对象,该对象将其功能委托给可复制类的一个实例,该类的引用在包装器中私自保存:
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};
}
模板
类包装器{
T&T;
公众:
包装器(T&T):T(T){}
T*运算符->(){return&T;}
};
包装器SomeHolder::getSomeMember(){
返回{some_member};
}
由于无法重载直接成员访问运算符,您需要更改调用代码以使用间接成员访问。我也建议使用智能指针,但我不希望使用引用成员。这样客户端就可以保留句柄
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;
};
模板
结构视图{
ViewPtr()=删除;
ViewPtr(T*tp):tp(tp){}
T*运算符->(){return tp;}
常量T*运算符->()常量{return tp;}
私人:
T*tp;
};
用法:
template <typename T>
class SomeHolder {
private:
T some_member;
public:
ViewPtr<T> getReference() {
return &some_member;
}
};
模板
类持有人{
私人:
T一些成员;
公众:
ViewPtr getReference(){
返回&some_成员;
}
};
这个Viewtr
也选择“传播常量”。也就是说:如果你有一个常量视图对象(或者如果它是一个成员,并且你在一个常量方法中),你会得到一个常量接口。我也建议使用智能指针,但我不喜欢使用引用成员。这样客户端就可以保留句柄
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;
};
模板
结构视图{
ViewPtr()=删除;
ViewPtr(T*tp):tp(tp){}
T*运算符->(){return tp;}
常量T*运算符->()常量{return tp;}
私人:
T*tp;
};
用法:
template <typename T>
class SomeHolder {
private:
T some_member;
public:
ViewPtr<T> getReference() {
return &some_member;
}
};
模板
类持有人{
私人:
T一些成员;
公众:
ViewPtr getReference(){
返回&some_成员;
}
};
这个Viewtr
也选择“传播常量”。也就是说:如果您有一个常量视图对象(或者如果它是一个成员,并且您使用的是一个常量方法),那么您将得到一个常量接口。行不是数字。不清楚哪些行是39和40。如果您想停止