问候所有人,
是否可以在C ++中创建一个面向未来的比较运算符(==)?
我遇到的问题是我们有一个有多个成员的班级。我们有一个比较运算符来验证对象的instance-1是否与instance-2具有相同的值。
即。我们可以做到
class blarg {
.....
};
.....
blarg b1(..initializers...);
blarg b2 = b1;
if (b1 == b2) {
... then do something ....
}
但是,我有一个同事在课程中添加了新成员,但未能更新比较运算符。这导致了我们需要一段时间才能解决的问题。
是否有编码练习,我的意思是 其他 而非代码审核(对我们来说失败),或编码方法,设计,模板,魔术豆,无论什么都可以帮助避免这样的情况?
我的第一反应是使用memcmp
命令。但是,在读取“Comparing structures in C vs C++”的堆栈溢出条目后,我发现这可能会有问题,因为C ++类不仅仅包含成员数据。
其他人如何处理这件事?
提前感谢您的帮助。
答案 0 :(得分:8)
嗯,显而易见的解决方案是在扩展原始类时要更加小心。 :)“更加小心”包括代码评论,类似的事情,但显然这不是万无一失的。
因此,从哲学的角度来解决这个问题,而不是技术问题,往往可以提供洞察力。在这种情况下的哲学是成为一个偏执的程序员。假设您今天编写的代码将在几个月或几年后被打破。 (编辑@Noah下面的评论:通常情况下,那个傻瓜就是我自己。作为一个偏执的程序员可能比其他人更能保护我我自己。如果你能做些什么来确保当在产品发货之前,nitwit确实会破坏您的代码,这会有所帮助。
我喜欢使用的两件事是静态断言和单元测试。可以在operator==
代码中使用静态断言来验证您的班级sizeof
是否符合您的预期。例如:
bool MyClass::operator==(const MyClass& rhs) const
{
static_assert(sizeof(MyClass) == sizeof(foo_) + sizeof(bar_))
...
}
...其中foo_
和bar_
是成员变量。当类的大小发生变化时,这将破坏编译。
由于静态断言通常采用模板类的形式,当表达式为false时将无法编译。这可能有点难以写(但这是一个有趣的练习 - 考虑如果你尝试将char test_[0]
成员添加到类中会发生什么)。幸运的是,这个轮子已经被发明了。有关示例,请参阅Boost,我认为新的MSVC编译器也附带一个。
答案 1 :(得分:7)
好的,现在回答。
对您的对象进行适当的单元测试应该完全解决了这种错误。这正是他们擅长指出的那种东西。
答案 2 :(得分:2)
解决这个问题的唯一真正方法是根本不需要为该类设置赋值运算符。那是什么,你问?如果我有动态内存等需要深层复制怎么办?
如果有人经常向您的类添加数据成员,则表示该类不是该数据的简单容器。通过将需要自定义赋值运算符的任何内容包装到一个单独的简单类中,您可以将自定义赋值代码放入该简单类中,替换需要使用该新简单类的实例进行自定义赋值的复杂类中的数据,并避免需要为复杂类编写自定义赋值运算符。
如果有的话,简单类可能不会经常更改,因此将会有更少的任务 - 操作员维护。可能性很大,经历这个过程也将最终为您留下更清洁,更可行的架构。
答案 3 :(得分:0)
在某些情况下,可以通过使所有成员成为某种形式的属性或类似形式来完成,可以在运行时单独进行迭代和比较。
对于正常的课程,这不是一个真正的选择 - 我建议对此进行单元测试,就像Noah Roberts在他的回答中指出的那样。
答案 4 :(得分:0)
swap
operator<<(std::ostream&, const blarg&)
,或其他形式的(反)序列化operator<
operator==
hash
功能在添加成员时运行检查清单,或者更改成员的类型或含义(或者可能删除一个成员,但在这种情况下,使用它的任何东西通常会自行爆炸,任何不使用它的东西可能会赢得'注意它已经消失了。
如果所有这些功能都被定义(或至少声明)在一起会更容易:当然,无论如何我都会将前三个“生命周期”事物保持在一起,而且如果不审查它们,我就永远不会触及成员。理想情况下,这三个“比较”理论也很接近,因为它们需要相互一致,所以一旦习惯根深蒂固,核对表最终可能会对课程进行简单的一瞥。 “我已经改变了课程结构,所以除了我已经想到的改变之外,我还需要看看这里要审查的功能......”
显然,如果你可以管理它,你就遵循开放/封闭原则,这种情况永远不会发生。就个人而言,我从未做过那样的事。