" get()const"与" getAsConst()const"
有人告诉我他们团队中C ++风格的差异。对于这个问题我有自己的看法,但是我会对每个人的利弊感兴趣。
因此,如果我们要通过两个getter(一个读/写,另一个是只读)公开一个类属性(即没有set方法)。至少有两种方法可以执行此操作:
class T ; class MethodA { public : const T & get() const ; T & get() ; // etc. } ; class MethodB { public : const T & getAsConst() const ; T & get() ; // etc. } ;
每种方法的利弊是什么?
我对C ++的技术/语义原因更感兴趣,但是样式原因也很受欢迎。
注意," MethodB"有一个主要的技术缺陷(提示:通用代码中)。
解决方案
虽然问题似乎只解决一种方法,但我很乐意就样式提供意见。就个人而言,出于风格原因,我更喜欢前者。大多数IDE会为我们弹出函数的类型签名。
在几乎所有情况下,C ++都应该完全有能力应对方法A。我一直使用它,而且我从来没有遇到任何问题。
在我看来,方法B是违反OneAndOnlyOnce的情况。并且,现在我们需要确定是否要处理const引用以编写首次编译的代码。
从技术上讲,我认为这是一种风格上的东西,但是MethodA使编译器的工作更加困难。对我来说,这是一件好事。
我希望第一个。当本质上做同一件事的两件事看起来相同时,在代码中看起来更好。另外,很少有一个非const对象,但是想调用const方法,这样就不用担心了(最坏的情况下,我们只需要const_cast <>)。
就我个人而言,我更喜欢第一种方法,因为它使界面更加一致。另外,对我来说,getAsConst()听起来和getAsInt()一样愚蠢。
另一方面,在返回指向类的数据成员的非常量引用或者非常量指针之前,我们确实应该三思。这是对人们利用班级内部运作的邀请,理想情况下应将其隐藏。换句话说,它破坏了封装。我将使用get()const和set(),并仅在没有其他方法或者确实有意义时(例如,授予对数组元素的读/写访问权限),才返回非常量引用或者矩阵。
第一个允许更改变量类型(无论是否为const),而无需进一步修改代码。当然,这意味着没有通知开发人员这可能已从预期路径更改。因此,真正重要的是我们是否重视能够快速重构或者拥有额外的安全网。
好吧,一方面,必须在" this"指针为const时调用getAsConst,而不是在要接收const对象的情况下调用。因此,除了任何其他问题外,它的名字也被巧妙地误用了。 (当" this"是非常量时,我们仍然可以调用它,但这不在这里或者那里。)
忽略这一点,getAsConst不会给我们带来任何收益,并且会给使用该接口的开发人员带来不必要的负担。现在,他必须确定自己是否正在使用const变量,以及是否要获取的新对象需要是const,而不仅仅是调用" get"并知道他正在获取所需的东西。然后,如果两个对象由于某种重构而变为非常量,则他必须退出呼叫。
给定标准库设置的样式先例(例如,begin()和begin()const仅举一个示例),很明显方法A是正确的选择。我质疑选择方法B的人的理智。
第二个是我个人不喜欢的与匈牙利表示法相关的东西,因此我会坚持第一种方法。
我不喜欢匈牙利表示法,因为它增加了我通常在编程中讨厌的冗余。这只是我的意见。
由于我们隐藏了班级的名称,因此这种思考方式可能适用也可能不适用:
告诉这两个对象MethodA和MethodB"获取"或者" getAsConst"是否有意义?我们是否将" get"或者" getAsConst"作为消息发送给任何一个对象?
我的看法是,作为消息的发送者/方法的调用者,我们就是那个获得价值的人。因此,响应此"获取"消息,我们正在向MethodA / MethodB发送一些消息,其结果就是我们需要获取的值。
示例:例如,如果MethodA的调用者是SOA中的服务,而MethodA是存储库,则在服务的get_A()内部,调用MethodA.find_A_by_criteria(...)。
因此,通常首选第一种样式。
但是,我们确实在我当前正在使用的代码库中使用了第二种样式的变体,因为我们希望在const使用和非const使用之间有很大的区别。
在我的特定示例中,我们有getTessellation和getMutableTessellation。它是通过写时复制指针实现的。出于性能方面的考虑,我们希望尽可能使用const版本,因此我们将名称缩写,并改用其他名称,这样人们无论何时都不打算写时,就不会意外地造成副本。
我看到的MethodB的主要技术缺点是,在向其应用通用代码时,我们必须将代码加倍以处理const版本和非const版本。例如:
假设T是一个可排序的对象(即,我们可以使用运算符<将它与类型T的对象进行比较),并且假设我们要查找两个MethodA(分别是两个MethodB)之间的最大值。
对于MethodA,我们只需要编写以下代码:
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 ; } }
此代码将与const对象和类型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版本和非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