返回对私有向量成员元素的非常量引用是否是一种不好的做法

Is it bad practice to return a non const reference to an element of a private vector member?

本文关键字:一种 是否是 引用 向量 成员 常量 非常 元素 返回      更新时间:2023-10-16

通过引用返回向量元素是不好的做法吗?

class X{
vector<Y> v;
public:
Y& getRefFromVectorOfY(unsigned int index){
retrun v.at(index);
}
void addToVectorOfY(Y y){
v.push_back(move(y));
}
};

虽然这是高效和干净的,但问题是它破坏了封装。 如果 v 是一个向量,并且是一个类的私有成员,则调用者现在将具有对私有向量中元素的引用,他们不仅可以读取该元素,还可以分配给(覆盖)。

可以做到这一点

x.getRefFromVectorOfY(0).setNumber(7);  //not bad..actually good

x.getRefFromVectorOfY(0) = move(Y2);   //very bad!

甚至

Y3 = move(x.getRefFromVectorOfY(0));   //OMG

当然,我们可以返回一个 const 和 ,只允许 const 操作。

按值返回元素的效率较低,因为它是副本。

如果 get 方法是将元素移出向量以按值返回,则向量将失去数据完整性,因为它将不再存储移出的数据(元素将重置为向量内的默认状态......所以。。。仍然留在矢量中,但作为垃圾数据)。

我为什么要问这个?透视

如果您遵循零规则,并且您在类中有一个私有向量成员,则需要方便的方法来 axs 和设置向量。 这就提出了如何返回值的问题。

通过 ref 返回的问题在于它会破坏封装,除非它是常量引用。

那么,我应该只返回一个 const ref 强制任何值设置使用 setVector 方法吗? 这样做的问题是有时您存储自定义类型...像Y..在向量中,您需要访问 Y 的非常量方法。 虽然 const 和保护向量元素在向量中不被重新设置或覆盖,但它也会防止调用方使用返回的元素非常量方法。

所以如果我只返回常量和...我做不到

x.getRefFromVectorOfY(0).setNumber(7);  //Y has a setNumber method

那将是一个问题。 这将是一个问题,因为我不想在 X 类中重新实现 Y 的任何方法。 这将是另一层间接和大量代码冗余。这反对有回报一个常量和一般政策。在许多情况下不切实际。

所以我必须根据返回值的恒定性进行覆盖,这是你不能做的。

所以我必须有两个带有差异名称的 get 函数,一个返回 const &...和其他只是返回 &. 感觉很脏。

const Y& getConstRefFromVectorOfY(unsigned int index){
retrun v.at(index);
}
Y& getRefFromVectorOfY(unsigned int index){
retrun v.at(index);
}

编辑

我添加了此表来总结问题目标

我希望 X 类的调用者能够安全有效地执行以下操作:

  • 模组矢量
  • 矢量中的模组元素
  • 读取向量中的元素

据我了解,夏天的选择:

">

-"是缺点,"+"是优点。

选项 1 -- 返回矢量元素的副本

MOD VECTOR
+ Can mod vector via class method addToVectorOfY
MOD ELEMENT IN VECTOR
- No way to mod Y element itself in vector without 
indirection & redundancy (re-implementation of some Y methods)!!!  
Because modifying returned copy does not modify what is in vector.
READ ELEMENT IN VECTOR
+ Yes, inefficiently can read element data by looking at copy of element

选项 2 -- 返回矢量元素的引用

MOD VECTOR
- Can mod vector in non-obious ways via returned ref to element. Breaks encapsulation.
Dangerous and also redundant because class has setter method to mod vector.
+ Can mod vector via class method addToVectorOfY
MOD ELEMENT IN VECTOR
+ Can call non-const methods of element returned
- Some danger to the reference being invalid upon vector resize
READ ELEMENT IN VECTOR
+ Yes, with maximum efficiency
- Some danger to the reference being invalid upon vector resize

选项 3 -- 返回矢量元素的常量引用

MOD VECTOR
+ Can mod vector via class method addToVectorOfY
MOD ELEMENT IN VECTOR
- No way to mod Y element itself in vector without 
indirection & redundancy (re-implementation of some Y methods)!!! 
READ ELEMENT IN VECTOR
+ Yes, with maximum efficiency
- Some danger to the reference being invalid upon vector resize 

有没有办法让 X 类的用户安全有效地完成所有 3 个?

好吧,std::vector自己的operator[]返回一个非常量引用,所以这本身并不是一个坏做法。具体来说,这不会"破坏封装" - 向量不应该封装掉它的成员,它应该提供对它们的访问。

此外,您的所有 3 个示例都很好,而不仅仅是第一个。它不是"非常糟糕"也不是"天哪"。

话虽如此,请记住,std::vector的存储会随着其大小的变化(至少在大小增加期间)而重新分配,因此它不能保证指针/引用的有效性。

因此,不好的是将引用(以及指针和不透明的迭代器)保留到可能调整大小的std::vector中。

通过引用返回向量元素是一种不好的做法吗?

是的,一般来说?在大多数情况下,我认为如果您花时间将类的成员变量封装为private以控制该变量的访问和操作,那么设计一个很容易破坏该控件的成员函数会使第一步变得毫无意义。根据用例的不同,这可能并不总是正确的,但在这里你提出了如此抽象的问题,以至于很难给出一个具体的答案。我可以在您的帖子中发现的唯一真正问题是:

按值返回元素效率较低,并且由于它是副本,因此效率较低。

我想这里要问的真正问题在保持对成员变量的更大访问控制与更直接地访问底层内存以便您可以更快地操作它之间是否存在有意义的、可衡量的性能差异?您是对的,通过引用返回在某些方面更有效,但这实际上对您的特定代码产生了实际影响吗?

此外,需要为要公开的私有成员变量维护什么级别的数据完整性也很重要。 Einpoklum 提出了一个很好的观点,即许多标准容器都遵循这种范式。它们对存储在容器中的值没有期望,只是保持对分配/删除它们所持有内存的控制。您的类可能对成员值采用的值有更强的控制要求。例如,如果该向量中的所有数据元素都需要是非负数,那么通过公开对该内存的引用,您将失去让类做出此类数据完整性保证的能力。这实际上只取决于需求,尽管我更喜欢根据需要有选择地释放对成员变量的控制,而不是在想要添加其他保证时提供完全访问权限并慢慢将其取消。

相关文章: