class A { /* */ };
class B {
private:
A mA;
};
假设我们不想在这里按值返回
B::mA
-我们想精确地操作B
内部的实例,调用其上的函数,等等。B::getA()
应该使用哪种类型?我们的选择是:A*
。可以在调用方删除,也可以存储对象的生存期。 A&
。如果调用者使用A a = b->getA()
而不是A& a = b->getA()
,则可能导致创建副本。 A&
加上explicit A::A(A&)
。禁止意外的副本创建,但现在A
取决于B
的实现细节。保留ref超过对象生存期的问题仍然存在。 我有想念吗?
最佳答案
我建议返回A&
-该方法的用户确实可以滥用它,但是对于C++中的几乎所有内容都是如此,并且返回对某些事物的引用是惯用的。
不过,如果您想使客户端更难(但并非并非没有可能)滥用getter,请考虑返回 std::reference_wrapper<A>
,这使得制作意外副本(*)更加困难:
std::reference_wrapper<A> a = some_b.get_a();
auto b = a; // doesn't copy `A` - it copies its address
A c = a; // copies, because `a` is implicitly convertible to `A&`
这种方法的缺点是必须使用
std::reference_wrapper::get()
来访问A
的成员(因为我们仍然没有可重载的operator.
)。(*):意外复制是最少的问题。即使
some_b.mA
死亡,用户也可以保留对some_b
的引用,从而创建悬挂的引用!这种错误是Rust由于其借用检查器而在编译时遇到的问题。如果有充分的理由使上述情况变得非常困难(但仍然并非不可能!),则可以使用高阶函数代替 setter/getter :
class A { /* */ };
class B {
private:
A mA;
public:
template <typename TF>
void mutate_a(TF&& f)
{
// <`static_assert` that `f` takes `A&` here>
f(mA);
}
};
用法:
B some_b;
some_b.mutate_a([](A& a)
{
// <do something with `a` here>
});
如您所见,很难将
a
带到lambda之外,但是语法开销仅在非常特殊的情况下才值得。我对返回A&
和“信任用户”的建议仍然有效。