我有一个包含大型成员变量的类。在我的例子中,大成员变量是一个包含许多对象的容器,它必须是私有的,因为我不想让用户直接修改
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.
};
这允许您创建一个小型可移植类,您不必担心复制,但也可以轻松访问较大的对象。你仍然需要担心缓存之类的事情,尽管