我可以使用const引用代替getter函数吗?

10

我想知道是否可以通过使用常量引用变量来绕过使用getter方法,具体如下:

#include <string>

class cTest
{
    private:
        int m_i;
        std::string m_str;

    public:
        const int & i;
        const std::string & str;

    cTest(void)
    : i(m_i)
    , str(m_str)
    {}
};

int main(int argc, char *argv[])
{
    cTest o;
    int i = o.i; // works
    o.i += 5; // fails
    o.str.clear(); // fails

    return 0;
}

我想知道为什么人们似乎根本不这样做。我是否遗漏了一些严重的缺点?请为优缺点列表做出贡献,如有必要,请进行更正。

优点:

  1. 没有通过getter函数调用产生的开销。
  2. 程序大小减小,因为有较少的函数。
  3. 我仍然可以修改类的内部,引用变量提供了一层抽象。

缺点:

  1. 与使用getter函数相比,我有一堆引用。这会增加对象大小。
  2. 使用const_cast,人们可以弄乱私有成员,但这些人是淘气的吧?
3个回答

7

确实存在一些严重的缺点(除了你提到的第二个缺点,我也将其归为“严重”类别):

1)您需要提供(并因此维护)一个复制构造函数:编译器默认值不起作用。

2)您需要提供一个赋值运算符:编译器默认值不起作用。

3)仔细考虑实现移动语义。同样,编译器默认值不起作用。

这三个问题意味着您提出的“const”引用反模式是不可行的。不要这样做!


5
更不用说,当你想要使用一种更有效的数据表示方式时,你却无法这样做,因为你需要对那些类型有一个有效的引用。 - chris
@chris:这是一个很好的观点。即使您有一个返回const引用的函数,如果您的数据模型发生变化,您也可以重构它以返回值副本。 - Bathsheba

2
获取函数的一个优点是,你可能在某个时间点想要更改返回值,而没有获取函数,这是不可能的。这种情况需要您实际上返回非引用,这在C++中不太常见。但是通过移动语义而不是引用,这应该是可行的。
您可能还想将断点放入获取函数中,以了解谁正在读取其值,您可能还想添加日志记录等。这被称为封装。
获取器的另一个优点是,在调试版本中,您可以对返回的数据添加额外的检查/断言。
最终编译器将内联您的getter函数,这将导致类似于您提出的代码的代码。
一些额外的缺点:
1)模板代码将使用函数调用来获取值,即size(),如果您将其更改为const&变量,则无法在某些模板中使用它。因此,这是一个一致性问题。

0

如果你想避免使用getter和setter,使用const引用成员不是解决方案。

相反,你需要确保周围结构的const正确性(这会自动给你提供对成员的const访问权限),并且让成员成为它们在逻辑上需要的任何东西。

请务必阅读有关getter和setter何时可以、应该或可以与公共数据成员交换的信息。例如,请参见this question。只要注意,如果你改变接口,调用getter不会影响调用站点,这是getter和setter所标榜的优势。现实似乎证明了相反的情况,例如,使用任何值得尊重的C++代码编辑器重构成员以及所有访问点是一个微不足道的操作。

虽然有人可能会主张封装,但我更强烈地主张const正确性,这减轻了很多封装的需求,并且真正简化了代码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接