1

有人告诉我他们团队中的 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有一个主要的技术缺陷(提示:在通用代码中)。

4

11 回答 11

10

C++ 应该完全能够在几乎所有情况下处理方法 A。我一直用它,从来没有遇到过问题。

方法B,在我看来,是一个违反OnceAndOnlyOnce的案例。而且,现在您需要弄清楚您是否正在处理 const 引用来编写第一次编译的代码。

我想这是一种风格上的事情——从技术上讲,它们都可以工作,但是 MethodA 使编译器工作得更加努力。对我来说,这是一件好事。

于 2008-09-18T21:24:23.630 回答
8

好吧,一方面,当“this”指针为 const 时,必须调用 getAsConst,而不是在您想要接收 const 对象时调用。因此,除了任何其他问题之外,它还被巧妙地命名为错误名称。(当 'this' 是非常量时,你仍然可以调用它,但这既不在这里也不在那里。)

忽略这一点,getAsConst 将一无所获,并且给使用该接口的开发人员带来了不应有的负担。而不是仅仅调用“get”并知道他得到了他需要的东西,现在他必须确定他当前是否正在使用 const 变量,以及他正在抓取的新对象是否需要是 const 。之后,如果两个对象由于一些重构而变为非常量,他必须切换他的调用。

于 2008-09-18T21:34:35.697 回答
4

就个人而言,我更喜欢第一种方法,因为它使界面更加一致。此外,对我来说 getAsConst() 听起来和 getAsInt() 一样愚蠢。

另一方面,在将非常量引用或非常量指针返回到类的数据成员之前,您确实应该三思而后行。这是邀请人们利用您班级的内部运作,理想情况下应该隐藏。换句话说,它打破了封装。我会使用 get() const 和 set(),并且仅在没有其他方法或确实有意义时才返回非常量引用,例如授予对数组元素的读/写访问权限或矩阵。

于 2008-09-18T21:26:01.707 回答
1

鉴于标准库设置的样式先例(即 begin() 和 begin() const 仅举一个例子),显然方法 A 是正确的选择。我质疑选择方法 B 的人的理智。

于 2008-09-18T21:37:55.283 回答
1

因此,通常首选第一种样式。

不过,我们确实在我目前正在处理的代码库中大量使用了第二种样式的变体,因为我们希望在 const 和非常量用法之间有一个很大的区别。

在我的具体示例中,我们有 getTessellation 和 getMutableTessellation。它是用写时复制指针实现的。出于性能原因,我们希望尽可能使用 const 版本,因此我们将名称缩短,并且我们将其命名为不同的名称,这样人们就不会在他们不打算写的时候意外地复制。

于 2008-09-18T23:45:00.767 回答
0

虽然您的问题似乎只解决了一种方法,但我很乐意就风格提供我的意见。就个人而言,出于风格原因,我更喜欢前者。大多数 IDE 会为您弹出函数的类型签名。

于 2008-09-18T21:22:21.513 回答
0

我更喜欢第一个。当本质上做相同事情的两件事看起来相同时,它在代码中看起来更好。此外,您很少有一个非常量对象但又想调用 const 方法,因此这并不是什么大问题(在最坏的情况下,您只需要一个 const_cast<>)。

于 2008-09-18T21:25:36.840 回答
0

第一个允许更改变量类型(无论是否const),而无需进一步修改代码。当然,这意味着没有通知开发人员这可能已从预期路径更改。因此,您真正看重的是能够快速重构还是拥有额外的安全网。

于 2008-09-18T21:27:07.400 回答
0

第二个是与匈牙利符号有关的东西,我个人喜欢,所以我会坚持一种方法。

我不喜欢匈牙利符号,因为它增加了我在编程中通常讨厌的冗余。这只是我的意见。

于 2008-09-18T21:43:13.460 回答
0

由于您隐藏了类的名称,因此这种关于风格的思考可能适用也可能不适用:

告诉这两个对象 MethodA 和 MethodB “get”还是“getAsConst”有意义吗?您会将“get”或“getAsConst”作为消息发送到任一对象吗?

在我看来,作为消息的发送者/方法的调用者,是获得价值的人;因此,为了响应这个“获取”消息,您正在向 MethodA / MethodB 发送一些消息,其结果是您需要获取的值。

示例:如果 MethodA 的调用者是 SOA 中的服务,而 MethodA 是存储库,则在服务的 get_A() 中,调用 MethodA.find_A_by_criteria(...)。

于 2008-09-18T21:55:20.763 回答
0

我看到的 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 ;
   }
}

此代码适用于 T 类型的 const 对象和非 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 和 non-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 ;
   }
}

结论,由于不信任编译器的 const 使用,我们负担了创建两个泛型函数的负担,而一个应该足够了。

当然,如果有足够的妄想症,第二个模板函数应该被称为 getMaxAsConst ......因此,问题将通过所有代码传播......

:-p

于 2008-09-19T16:38:28.900 回答