我有一个执行某些计算的模块,在计算过程中,它与其他模块通信。由于计算模块不想依赖其他模块,因此它会公开这样的接口(当然这是一个非常简化的版本):
class ICalculationManager
{
public:
double getValue (size_t index) = 0;
void setValue (size_t index, double value) = 0;
void notify (const char *message) = 0;
};
想要使用计算模块的应用程序需要编写自己的接口实现,并将其提供给计算工具,如下所示:
MyCalculationManager calcMgr;
CalculationTool calcTool (calcMgr);
calcTool.calculate();
我现在想知道将“const”添加到ICalculationManager接口的方法是否有意义。
似乎合乎逻辑的是,getValue方法只能得到一些东西并且不会改变任何东西,所以我可以制作这个const。并且setValue可能会更改数据,因此不会是const。 但对于像通知这样的更通用的方法,我无法确定。
实际上,对于所有方法我现在都可以确定该方法实际上是作为const方法实现的,如果我将接口方法设为const,我也强制所有实现都是const,这是可能不想要。
在我看来,如果你事先知道你的实现是什么以及它是不是const,那么const方法才有意义。这是真的吗?
将这种接口的方法设为const是否有意义?如果它有意义,那么确定方法是否应该是const的好规则是什么,即使我不知道实现是什么?
编辑:将参数从“char *”更改为“const char *”,因为这会导致无关的答案。
答案 0 :(得分:5)
当您向客户广告时,您会创建一个函数const
,调用该函数永远不会更改对象的外部可见状态。您的对象只有一个可以检索的状态getValue。
因此,如果getValue可以导致下一个getValue返回不同的值,那么确定,保持非const。如果你想告诉客户端调用getValue()永远不会改变下一个getValue()返回的值,那么将它设为const。
同样适用于通知:
double d1 = mgr->getValue(i);
mgr->notify("SNTH"); // I'm cheating.
double d2 = mgr->getValue(i);
assert(d1==d2);
如果对于所有情况都应该如此,那么所有i,那么notify()应该是const。否则它不应该。
答案 1 :(得分:4)
是。人们应该随时随地使用const
这样做。执行计算的方法(这是你的界面建议的)应该改变它的可观察行为是没有意义的,因为它有“通知”调用它。 (就此而言,通知如何与计算有关?)
我创建了一个界面成员const
,您不强迫客户const
- 您只允许他们使用const ICalculationManager
。
我可能会Notify
const
。如果客户需要在通知的情况下执行某些操作non-const
,那么Notify
不是一个好的方法名称 - 该名称建议非状态修改转换,例如日志记录,而不是修改。
例如,大多数时候你通过你的界面,你会想要使用pass-by-reference-to-const来传递接口实现者,但是如果这些方法不是{{1}你不能这样做。
答案 2 :(得分:2)
接口应该指导实现,而不是相反。如果你还没有确定方法或参数是否可以是const,那么你就没有完成设计。
使用const是一种关于代码是允许还是不允许执行的断言的方法。这在推理一段代码时非常有价值。例如,如果notify
的参数不是常量,它会对消息做出哪些更改?如果需要,它将如何使信息更大?
编辑:您似乎知道声明const参数的值,因此请在此基础上进行构建。假设您想要一个函数来记录计算的值:
void RecordCalculation(const ICalculationManager *calculation);
您可以在该指针上调用的唯一方法是const方法。您可以确定在函数返回后,对象将保持不变。这就是我对代码进行推理的意思 - 你可以绝对肯定对象不会被改变,因为如果你尝试,编译器会产生错误。
编辑2:如果您的对象包含一些内部状态,该状态将被修改以响应逻辑上为const的操作(例如缓存或缓冲区),请继续使用mutable
这些成员的关键字。这就是它的发明。
答案 3 :(得分:0)
对我来说,这只取决于你的界面合同。
对于getter方法,我不明白为什么它应该更改任何数据,如果发生这种情况,可能是可选的。
对于setter方法我同意,而不是const,因为这肯定会以某种方式改变数据。
对于通知很难说,如果不知道它对您的系统意味着什么。此外,您是否希望实现修改message参数?如果现在,它也应该是const。
答案 4 :(得分:0)
无需阅读整篇文章:是的,如果你想在const上下文中使用一个对象(继承ICalculationManager),这是有意义的。通常,如果不操作私有数据,则应始终使用const限定符。
编辑: 就像Mark Ransom所说的那样:你需要确切知道你的界面功能应该如何表现,否则你就没有完成设计。
答案 5 :(得分:-3)
我知道我会为此获得很多downvotes,但在我看来,C ++中const-correctness的有用性被夸大了。 const的想法是原始的(它只捕获了一点概念......改变/不改变)并且具有高成本,甚至包括代码重复的必要性。它也不能很好地扩展(考虑const_iterators)。
更重要的是,我甚至不记得一个案例(甚至不是一个案例),其中const-correctness机制通过发现一个真正的逻辑错误帮助我,也就是说我试图做一些我不应该做的事情。相反,编译器每次阻止我在const声明部分都有一个问题(即我试图做的事情在逻辑上是合法的,但是方法或参数在关于const-ness的声明中有问题)。 在所有情况下,我都记得在哪里得到了与const-correctness相关的编译错误,修复只是添加了一些缺失的const关键字或删除了一些过多的...没有使用const-correctness的想法那些错误不会是在那里。
我喜欢C ++,但我当然不喜欢死亡(离题:当我采访某人时,我经常会问的问题是“你不喜欢的部分是什么<语言>? “......如果答案是”无“,那么只是意味着我正在与之交谈的人仍处于狂热阶段并且显然没有很大的实际经验。
C ++的许多部分非常好,IMO可怕的部分(例如流格式化)和不太可怕的部分,但既不是逻辑上美观也不实用。在这个灰色区域中,正确性概念是IMO(这不是一个新手的印象......我在C ++编写了许多行和多年之后得出了这个结论)。 可能是我,但显然const正确性解决了我的大脑没有的问题......我还有很多其他的问题,但是当我应该更改实例状态和不应该时,这不是令人困惑的问题。 / p>
不幸的是(与流格式不同)你不能忽略C ++中的const-correctness机制,因为它是核心语言的一部分,所以即使我不喜欢它,我也不得不遵守它。
你现在可以说......好吧,但这个问题的答案是什么?简单地说,我不会对语义描述的那一部分感到太疯狂......它只是一点点而且价格高昂;如果你不确定并且你可以在不宣布常数的情况下逃脱,那么就不要这样做。引用或方法的常量从来不是编译器的帮助(请记住它可以合法地转换)并且它已被添加到C ++中,作为程序员的帮助。然而,我的经验告诉我(鉴于高成本和低回报),这根本不是真正的帮助。