C++ 安全地返回对成员变量的引用
在我正在设计的库中,我有时需要对类的大型成员变量进行读取访问。由于它们的大小,我不想创建通过复制成员返回的getter。我不希望它们可以从外部修改,因此我不能将它们公开或返回对它们的引用。所以我想我应该用一个“阅读器”: (这并不是真正的双打) 但在这里,有人可以const_cast引用并直接访问数据。即使没有恶意意图,也有人可以保护对数据成员的引用,并在原始对象超出范围后保留它。我知道const引用可以保持一个临时的可行,但这并不能消除const_cast-problem。 所以我想出了一个解决办法:C++ 安全地返回对成员变量的引用,c++,templates,C++,Templates,在我正在设计的库中,我有时需要对类的大型成员变量进行读取访问。由于它们的大小,我不想创建通过复制成员返回的getter。我不希望它们可以从外部修改,因此我不能将它们公开或返回对它们的引用。所以我想我应该用一个“阅读器”: (这并不是真正的双打) 但在这里,有人可以const_cast引用并直接访问数据。即使没有恶意意图,也有人可以保护对数据成员的引用,并在原始对象超出范围后保留它。我知道const引用可以保持一个临时的可行,但这并不能消除const_cast-problem。 所以我想出了一个解
#include <iostream>
template<class T>
class SafeMemberReference
{
public:
using type = T;
SafeMemberReference(const T& t) :t(t) {}
explicit SafeMemberReference(T&& t) = delete;
operator const T& () && {return t; }
T get() && {return t; }
private:
const T& t;
};
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
SafeMemberReference<double> readD() const { return d_; }
private:
double d_;
};
int main()
{
TestClass foo(1.2);
// temporary from read can be used as temporary in expressions
std::cout << foo.readD() << std::endl;
// temporary can be used to copy from
auto x = foo.readD().get();
// lvalue can not be used, so a possible dangling reference is no problem
auto ref = foo.readD();
//std::cout << ref << std::endl;
}
#包括
模板
类安全成员引用
{
公众:
使用类型=T;
安全成员引用(常量T&T):T(T){
显式安全成员引用(T&&T)=删除;
运算符常量T&(&&{return T;}
T get()&{return T;}
私人:
康斯特T&T;
};
类TestClass
{
公众:
显式TestClass(双d):d_d(d){}
SafeMemberReference readD()常量{return d_;}
私人:
双d_;
};
int main()
{
测试类别foo(1.2);
//“从读取临时”可以用作表达式中的临时
任何试图对抗语言本身的解决方案都不是好的解决方案
如果他们以这种方式使用了const\u cast
,他们应该得到一个教训:试图通过const\u cast
在一个最初声明为const
的对象上更改一个对象的行为是未定义的。即使你设法想出一个解决方案来防止这种情况发生,一个怀有敌意的程序员仍然可以采取行动对象的地址,偏移该地址(使用无符号字符*
指针算法)并通过该指针修改数据成员
因此,如果我是你,我不会反对这种语言。如果我是你,按照你最初的建议,返回一个const
引用
代码静态分析工具/编译器警告/代码审查/人力资源部门将帮助您保持其他协作程序员的直线性和狭隘性。任何试图对抗语言本身的解决方案都不是一个好的解决方案
如果他们以这种方式使用了const\u cast
,他们应该得到一个教训:试图通过const\u cast
在一个最初声明为const
的对象上更改一个对象的行为是未定义的。即使你设法想出一个解决方案来防止这种情况发生,一个怀有敌意的程序员仍然可以采取行动对象的地址,偏移该地址(使用无符号字符*
指针算法)并通过该指针修改数据成员
因此,如果我是你,我不会反对这种语言。如果我是你,按照你最初的建议,返回一个const
引用
代码静态分析工具/编译器警告/代码审查/人力资源部门将帮助您保持其他协作程序员的直截了当。安全成员引用解决了什么问题?SafeMemberReference
既没有解决悬空引用的问题,也没有避免不必要的复制,也没有防止修改使用const\u cast
。也可以直接返回一个副本或const&
。您有数据要处理。您有一个封装它的类。为什么不像这个类方法那样实现所有需要的数据操作,而不直接访问数据。SafeMemberReference
解决了什么问题?它也没有解决问题使用const\u cast
避免不必要的副本或阻止修改的悬空引用的问题。最好直接返回副本或const&
。您有数据要处理。您有一个封装它的类。为什么不像此类方法那样实现所有需要的数据操作,而无需直接访问到数据。有关何时const\u cast
可以的更多信息,您可能需要阅读、一个问题和一个问题。出于好奇:在OP的情况下,这真的是未定义的行为吗?如果原始对象是非const(哪个OP的d
),则表示const\u cast
是安全的。有关const_cast
何时可以的更多信息,您可能需要阅读、一个问题和一个问题。出于好奇:OP的情况下这真的是未定义的行为吗?如果原始对象是非常量(哪个OP的d_
),则表示const_cast
是安全的。
#include <iostream>
template<class T>
class SafeMemberReference
{
public:
using type = T;
SafeMemberReference(const T& t) :t(t) {}
explicit SafeMemberReference(T&& t) = delete;
operator const T& () && {return t; }
T get() && {return t; }
private:
const T& t;
};
class TestClass
{
public:
explicit TestClass(double d): d_(d){}
SafeMemberReference<double> readD() const { return d_; }
private:
double d_;
};
int main()
{
TestClass foo(1.2);
// temporary from read can be used as temporary in expressions
std::cout << foo.readD() << std::endl;
// temporary can be used to copy from
auto x = foo.readD().get();
// lvalue can not be used, so a possible dangling reference is no problem
auto ref = foo.readD();
//std::cout << ref << std::endl;
}