我可以使用 const 引用而不是 getter 函数吗?
Can I use const references instead of getter functions?
我只是想知道如果我只允许一个const引用变量,我是否可以绕过使用getters,如下
#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;
}
我想知道为什么人们似乎根本不这样做。我缺少一些严重的缺点吗?请贡献优点和缺点列表,并在必要时进行纠正。
优点:
- 通过调用 getter 函数没有开销。
- 由于功能较少,程序大小有所减少。
- 我仍然可以修改 class 的内部结构,引用变量提供了一个抽象层。
缺点:
- 而不是 getter 函数,我有一堆参考。这会增加对象大小。
- 使用
const_cast
,人们可以把私人成员搞得一团糟,但这些人很恶作剧,对吧?
确实有一些严重的缺点(除了你也提到的第二个缺点,我也把它放在 "severe" 类别中):
1) 您需要提供(并因此维护)一个复制构造函数:编译器默认情况下将不起作用。
2) 您需要提供一个赋值运算符:编译器默认情况下不起作用。
3) 仔细考虑实现移动语义。同样,编译器默认值将不起作用。
这三件事意味着您提出的 const
参考反模式是行不通的。不要这样做!
getter 函数的一个优点是您可能在某个时间点 - 想要更改 returned 值 - 而没有 getter 函数你无法做到。这个场景实际上需要你 return 非引用,这在 c++ 中不太常见。 [edit] 但是使用移动语义而不是引用这应该是可行的[/edit]
您可能还想在 getter 函数中放置一个断点以了解谁在读取它的值,您可能想添加日志记录等。这称为
封装。
getter 的另一个优点是,在调试版本中,您可以在 returned 数据上添加额外的 checks/asserts。
最终编译器将内联您的 getter 函数,这将生成与您建议的代码相似的代码。
一些额外的缺点:
1) 模板代码将希望使用函数调用获取值,即。 size()
,如果你改成const& variable,那么你将无法在某些模板中使用它。所以这是一个一致性问题。
如果您想避免 getters 和 setter,使用 const 引用成员不是解决方案。
相反,您需要确保周围结构的 const 正确性(这会自动为您提供对成员的 const 访问权限),并让成员成为逻辑上需要的任何成员。
请务必阅读 getter 和设置器何时可以、应该或可以与 public 数据成员切换。参见例如this question。请注意,如果您更改界面,setters/getters 的先驱优势是调用 getter 不会影响调用站点。现实似乎并非如此,例如重构成员及其所有访问点对于任何自尊的 C++ 代码编辑器来说都是微不足道的操作。
虽然有人会赞成封装,但我更强烈地赞成 const 的正确性,这减轻了对大量封装的需求,并确实大大简化了代码。
我只是想知道如果我只允许一个const引用变量,我是否可以绕过使用getters,如下
#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;
}
我想知道为什么人们似乎根本不这样做。我缺少一些严重的缺点吗?请贡献优点和缺点列表,并在必要时进行纠正。
优点:
- 通过调用 getter 函数没有开销。
- 由于功能较少,程序大小有所减少。
- 我仍然可以修改 class 的内部结构,引用变量提供了一个抽象层。
缺点:
- 而不是 getter 函数,我有一堆参考。这会增加对象大小。
- 使用
const_cast
,人们可以把私人成员搞得一团糟,但这些人很恶作剧,对吧?
确实有一些严重的缺点(除了你也提到的第二个缺点,我也把它放在 "severe" 类别中):
1) 您需要提供(并因此维护)一个复制构造函数:编译器默认情况下将不起作用。
2) 您需要提供一个赋值运算符:编译器默认情况下不起作用。
3) 仔细考虑实现移动语义。同样,编译器默认值将不起作用。
这三件事意味着您提出的 const
参考反模式是行不通的。不要这样做!
getter 函数的一个优点是您可能在某个时间点 - 想要更改 returned 值 - 而没有 getter 函数你无法做到。这个场景实际上需要你 return 非引用,这在 c++ 中不太常见。 [edit] 但是使用移动语义而不是引用这应该是可行的[/edit]
您可能还想在 getter 函数中放置一个断点以了解谁在读取它的值,您可能想添加日志记录等。这称为 封装。
getter 的另一个优点是,在调试版本中,您可以在 returned 数据上添加额外的 checks/asserts。
最终编译器将内联您的 getter 函数,这将生成与您建议的代码相似的代码。
一些额外的缺点:
1) 模板代码将希望使用函数调用获取值,即。 size()
,如果你改成const& variable,那么你将无法在某些模板中使用它。所以这是一个一致性问题。
如果您想避免 getters 和 setter,使用 const 引用成员不是解决方案。
相反,您需要确保周围结构的 const 正确性(这会自动为您提供对成员的 const 访问权限),并让成员成为逻辑上需要的任何成员。
请务必阅读 getter 和设置器何时可以、应该或可以与 public 数据成员切换。参见例如this question。请注意,如果您更改界面,setters/getters 的先驱优势是调用 getter 不会影响调用站点。现实似乎并非如此,例如重构成员及其所有访问点对于任何自尊的 C++ 代码编辑器来说都是微不足道的操作。
虽然有人会赞成封装,但我更强烈地赞成 const 的正确性,这减轻了对大量封装的需求,并确实大大简化了代码。