“get() const” 对比“getAsConst() const”
-
01-07-2019 - |
题
有人告诉我他们团队中 C++ 风格的差异。我对这个问题有自己的观点,但我感兴趣的是 优点 和 缺点 来自每个人。
因此,如果您有一个类属性,您想通过两个 getter 来公开,一个是读/写的,另一个是只读的(即没有固定的方法)。至少有两种方法可以做到这一点:
class T ;
class MethodA
{
public :
const T & get() const ;
T & get() ;
// etc.
} ;
class MethodB
{
public :
const T & getAsConst() const ;
T & get() ;
// etc.
} ;
每种方法的优点和缺点是什么?
我对 C++ 技术/语义原因更感兴趣,但风格原因也很受欢迎。
注意 MethodB
有一个主要的技术缺陷(提示:在通用代码中)。
解决方案
嗯,一方面,getAsConst 必须 当“this”指针为 const 时调用,而不是当您想要接收 const 对象时调用。因此,与其他问题一样,它的命名也被巧妙地错误了。(当“this”是非常量时,您仍然可以调用它,但那既不在这里也不在那里。)
忽略这一点, getAsConst 不会为您带来任何好处,并且会给使用该接口的开发人员带来过度的负担。现在他必须确定他当前是否正在使用 const 变量,以及他获取的新对象是否需要为 const,而不是仅仅调用“get”并知道他正在获取所需的内容。之后,如果两个对象由于某些重构而变得非常量,他就必须关闭他的调用。
其他提示
C++ 应该能够在几乎所有情况下完美地应对方法 A。我一直用它,从来没有遇到过问题。
在我看来,方法B违反了OnceAndOnlyOnce。而且,现在您需要弄清楚是否正在处理 const 引用来编写第一次编译的代码。
我想这是一个风格上的问题 - 从技术上讲,它们都有效,但 MethodA 使编译器工作得更加困难。对我来说,这是一件好事。
就我个人而言,我更喜欢第一种方法,因为它可以使界面更加一致。另外,对我来说 getAsConst() 听起来和 getAsInt() 一样愚蠢。
另一方面,在返回非常量引用或指向类的数据成员的非常量指针之前,您确实应该三思而后行。这是邀请人们利用你们班级的内部运作,最好是隐藏起来。换句话说,它破坏了封装。我会使用 get() const 和 set(),并且仅在没有其他方法或确实有意义时才返回非常量引用,例如提供对数组元素的读/写访问权限或矩阵。
鉴于标准库设置的样式先例(即 begin() 和 begin() const 仅举一个示例),很明显方法 A 是正确的选择。我怀疑选择方法B的人是否理智。
因此,通常首选第一种样式。
不过,在我当前正在处理的代码库中,我们确实使用了第二种样式的变体,因为我们希望在 const 和非常量用法之间有很大的区别。
在我的具体示例中,我们有 getTessellation 和 getMutableTessellation。它是通过写时复制指针实现的。出于性能原因,我们希望尽可能使用 const 版本,因此我们使名称更短,并使用不同的名称,这样人们就不会在不打算写入时意外地产生副本。
虽然您的问题似乎只涉及一种方法,但我很乐意就风格提供意见。就我个人而言,出于风格原因,我更喜欢前者。大多数 IDE 都会为您弹出函数的类型签名。
我更喜欢第一个。当本质上做同样事情的两件事看起来相同时,它在代码中看起来更好。另外,你很少有一个非 const 对象但想调用 const 方法,所以这没什么大不了的(在最坏的情况下,你只需要一个 const_cast<>)。
第一个允许更改变量类型(无论是 const
或不),无需进一步修改代码。当然,这意味着不会通知开发人员这可能已偏离预期路径。因此,这实际上取决于您是否重视能够快速重构,或拥有额外的安全网。
第二个是与匈牙利表示法有关的东西,我个人是这样的 不 就像这样我会坚持 第一的 方法。
我不喜欢匈牙利表示法,因为它增加了我在编程中通常讨厌的冗余。这只是我的意见。
由于您隐藏了类的名称,因此这种风格思考的内容可能适用,也可能不适用:
告诉这两个对象 MethodA 和 MethodB“get”或“getAsConst”有意义吗?您会将“get”或“getAsConst”作为消息发送到任一对象吗?
在我看来,作为消息的发送者/方法的调用者, 你 是获得价值的人;因此,为了响应此“获取”消息,您将向 MethodA / MethodB 发送一些消息,其结果就是您需要获取的值。
例子:如果 MethodA 的调用者是 SOA 中的服务,并且 MethodA 是存储库,则在服务的 get_A() 内,调用 MethodA.find_A_by_criteria(...)。
我看到的 MethodB 的主要技术缺点是,当对其应用泛型代码时,我们必须加倍代码才能处理 const 和非 const 版本。例如:
假设 T 是一个可排序的对象(即,我们可以使用运算符 < 与 T 类型的对象进行比较),假设我们想要找到两个 MethodA 之间的最大值(分别为:两种方法B)。
对于方法A,我们需要编写的代码是:
template <typename T>
T & getMax(T & p_oLeft, T & p_oRight)
{
if(p_oLeft.get() > p_oRight.get())
{
return p_oLeft ;
}
else
{
return p_oRight ;
}
}
此代码适用于 T 类型的 const 对象和非常量对象:
// Ok
const MethodA oA_C0(), oA_C1() ;
const MethodA & oA_CResult = getMax(oA_C0, oA_C1) ;
// Ok again
MethodA oA_0(), oA_1() ;
MethodA & oA_Result = getMax(oA_0, oA_1) ;
当我们想要将这个简单的代码应用于遵循 MethodB 约定的内容时,问题就出现了:
// NOT Ok
const MethodB oB_C0(), oB_C1() ;
const MethodB & oB_CResult = getMax(oB_C0, oB_C1) ; // Won't compile
// Ok
MethodA oB_0(), oB_1() ;
MethodA & oB_Result = getMax(oB_0, oB_1) ;
为了让 MethodB 在 const 和非常量版本上都工作,我们必须都使用已经定义的 getMax,但添加以下版本的 getMax:
template <typename T>
const T & getMax(const T & p_oLeft, const T & p_oRight)
{
if(p_oLeft.getAsConst() > p_oRight.getAsConst())
{
return p_oLeft ;
}
else
{
return p_oRight ;
}
}
结论是,由于不信任编译器的常量使用,我们在创建两个泛型函数时给自己带来了负担,而实际上一个函数就足够了。
当然,如果有足够的偏执,第二个模板函数应该被称为 getMaxAsConst...因此,问题将通过所有代码自行传播......
:-p