是否定期使用const_cast作为设计工具?

时间:2014-08-10 18:41:08

标签: c++ const-cast

我查看了下面的代码类型,虽然我对问题(*)有个人答案,但我希望收到C ++ /设计专家的意见。

出于某种原因,Data是一个具有不可修改标识符和可修改值的对象:

class Data
{
   const Id    m_id ;        // <== note that m_id is a const member variable
   Value       m_value ;

   Data(const Id & id, const Value & value) ;

   Data(const Data & data) ;
   Data & operator = (const Data & data) ;

   // etc.
} ;

设计选择成为语言选择,因为标识符在类级别(**)被声明为const,以避免它(意外)修改,甚至在类成员函数内部...

...但是正如您所看到的,有一个复制赋值运算符,它实现为:

Data & Data::operator = (const Data & that)
{
   if(this != &that)
   {
      const_cast<Id &>(this->m_id) = that.m_id ;
      this->m_value                = that->m_value ;
   }

   return *this ;
}

复制赋值运算符不是const限定的事实使得此代码安全(用户只能合法地在非const对象上调用此方法而不会引发未定义的行为)。

但是使用const_cast来修改其他const成员变量是C ++中一个好的类设计选择吗?

我想强调以下几点:

  • 数据显然是值类型(它具有operator =成员函数)
  • 在这种模式中,其他一些函数也可以合法地需要const_cast(例如,移动构造函数/赋值和/或交换函数),但不是很多。

请注意,这可能是一个代码审核问题,但这不是一个&#34;日常&#34;码。这是一个通用的C ++类型设计问题,需要平衡语言的需求/功能和模式/习语的代码解释。

另请注意,mutable(如在C ++ 98中)不是问题的解决方案,因为目标是使成员变量尽可能不可修改。当然,mutable(如在C ++ 11帖子中 - &#34;你不知道constmutable&#34;由Herb Sutter提供)甚至更少解决方案。

(*)我可以私下将我对该问题的回答转发给任何要求的人。

(**)另一个解决方案是使对象非const,并使其在接口级别为const(即不提供可以改变它的函数) < / p>

4 个答案:

答案 0 :(得分:6)

来自cppreference:

  

尽管const_cast可以从任何指针或引用中删除constness或volatile,但是使用结果指针或引用来写入声明为const的对象或访问声明为volatile的对象会调用未定义的行为。

这意味着您的副本分配不安全,但显然不正确。如果您声明某些内容const,则无法安全地更改它。这与设计无关。

const_cast的唯一有效用途是从const引用中删除constness或指向非const对象的指针(或const对象,然后不修改它,但是你不能const_cast而不是)。

答案 1 :(得分:0)

当我向私有成员实现单独的访问器时,我将使用它,它返回一个const引用,即该类的客户端只看到一个const引用。

然而,当派生类需要&#34;修改&#34;私有成员,我可以实现一个非const受保护的访问器,但我宁愿将派生类的访问器调用限制为const-reference,大多数时候它只需要const引用。

那么,在我确实需要的几种情况下,&#34;调整&#34;它在派生类中,const_cast&lt;&gt;伸出拇指,但这是选择。我喜欢它坚持下去。我可以轻松地搜索它(谁是const_cast&lt;&gt; -ing this class?)。

替代方案 - 提供受保护的非常规访问器,可能更多&#34;正确&#34;在语法上,但我宁愿使非常量访问变得突兀,而不是普通&#34;。

答案 2 :(得分:0)

通常一个班级应该对自己的成员有完全的控制和知识。您要求保护成员免于在其自己的类中出现错误,这违反了一些基本的面向对象设计原则。

原因之一是,如果私有变量的非常不变,则可以将其声明为常量。但在您的情况下,您只想保护它免受某些方法的影响。在这种情况下,保持非常数,或拆分类。您可以使用类似Private class data pattern的内容来更好地控制变量的可访问性。

答案 3 :(得分:0)

即使我不是设计专家,更不用说C ++专家,我也认为这是“设计陷阱”的情况(我可以这样说,因为某些陷阱是巧妙地完成的)。

在我看来,争论是从错误的假设开始的,即“数据显然是一种值类型”,然后变成一些“常量”问题。

Data对象的“值”是IDValue的组合,以及Value的“ 可键性Id确定(IdValue)对的唯一性。 换句话说,是 Id-> Value 对应关系将其自身描述为常量,但是在 univocal 的意义上。

此外,如果将Data对象作为 Id-> Value 对应关系出生,由于某种原因该对应关系不再有效(必须修改),则{{1 }}本身已经结束了其生命周期,因此它不会改变。从这个角度来看,我们来描述不可变对象的特征。

我将用以下代码来实现它,其中Data类模板通过从引用返回的对象池中进行绘制来封装上面概述的要求:

KeyedValue