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&和“信任用户”的建议仍然有效。

    10-07 17:12