我查看了下面的代码类型,虽然我对问题(*)有个人答案,但我希望收到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;你不知道const
和mutable
&#34;由Herb Sutter提供)甚至更少解决方案。
(*)我可以私下将我对该问题的回答转发给任何要求的人。
(**)另一个解决方案是使对象非const,并使其在接口级别为const(即不提供可以改变它的函数) < / p>
答案 0 :(得分:6)
尽管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
对象的“值”是ID
和Value
的组合,以及Value
的“ 可键性” Id
确定(Id
,Value
)对的唯一性。
换句话说,是 Id-> Value 对应关系将其自身描述为常量,但是在 univocal 的意义上。
此外,如果将Data
对象作为 Id-> Value 对应关系出生,由于某种原因该对应关系不再有效(必须修改),则{{1 }}本身已经结束了其生命周期,因此它不会改变。从这个角度来看,我们来描述不可变对象的特征。
我将用以下代码来实现它,其中Data
类模板通过从引用返回的对象池中进行绘制来封装上面概述的要求:
KeyedValue