用于不复制的大型成员变量的Getter



我有一个包含大型成员变量的类。在我的例子中,大成员变量是一个包含许多对象的容器,它必须是私有的,因为我不想让用户直接修改

class Example {
public:
std::vector<BigObject> get_very_big_object() const { return very_big_object; }
private:
std::vector<BigObject> very_big_object;
}

我希望用户能够在不复制的情况下查看对象:

Example e();
auto very_big_object = e.get_very_big_object();  // Uh oh, made a copy
cout << very_big_object[11];  // Look at any element in the vector etc

我对最好的方法有点困惑。我想返回一个常量引用,即使我的getter:

const std::vector<BigObject>& get_very_big_object() const { return very_big_object; }

我读过这篇文章,认为它可能有风险,智能指针std::unique_ptr可能会更好,但使用现代C++11移动语义可以最好地解决这个问题。但我觉得这有点神秘。

做这件事的现代最佳实践是什么?

我读过这篇文章,认为它可能有风险,智能指针std::unique_ptr可能更好,但这个问题可以使用现代C++11移动语义来最好地解决。

在这一点上,这篇文章完全错了。智能指针并不能消除"风险"。

文章相关部分的快速摘要

  • 如果类返回对数据成员的const引用,则客户端代码可能引入const_cast,从而在不经过类的API的情况下更改数据成员
  • 文章提出(错误地(可以通过使用智能指针来避免上述情况。设置是为了让类维护一个指向数据的共享指针,并让getter将该指针转换为指向const数据的共享指示器

对要点的批评

首先,这是行不通的。所要做的就是取消对智能指针的引用,以获得对数据的const引用,然后可以像以前一样是const_cast。使用作者自己的例子,而不是

std::string &evil = const_cast<std::string&>(obj.someStr());

使用

std::string &evil = const_cast<std::string&>(*obj.str_ptr());

以在返回智能指针时获得相同的数据更改结果。整篇文章没有错,但确实有几点错了。这是其中之一。

其次,这不是你关心的问题。当您返回一个const引用时,您告诉客户端代码这个值不会被更改。如果客户端代码无论如何都这样做了,那么就是客户端代码破坏了协议。从本质上讲,客户端代码调用了未定义的行为,因此您的类可以自由地执行任何操作,甚至使程序崩溃。

实现这一点的现代最佳实践是什么?

只需返回一个const引用。(大多数规则都有例外,但根据我的经验,这条规则似乎在95-99.9%的时间内达到了目标。(

当我为学校开发BDD库时,我所做的是创建一个名为VeryBigObject的包装类,它在实例化时联系单例并隐藏引用计数指针,从那里你可以覆盖operator->()方法,以便直接访问类的方法。

所以像这个

class VeryBigObject {
private:
vector<BigObject>* obj;
public:
VeryBigObject() {
// find a way to instantiate with a pointer, not by copying
}
VeryBigObject(const VeryBigObject& o) {
// Update reference counts
obj = o.obj;
}
virtual VeryBigObject operator->(const VeryBigObject&); // I don't remember how to do this one, just google it.
... // Do other overloads as you see fit to mask working with the pointer directly.
};

这允许您创建一个小型可移植类,您不必担心复制,但也可以轻松访问较大的对象。你仍然需要担心缓存之类的事情,尽管

最新更新