如何在 c++ 中计算对象的哈希/校验和/指纹?

How can I calculate a hash/checksum/fingerprint of an object in c++?

本文关键字:哈希 校验和 指纹 对象 c++ 计算      更新时间:2023-10-16

如何在c++ 中计算对象的哈希/校验和/指纹?

要求:

该函数必须是"单射"(*(。换句话说,不应该有两个不同的输入对象,它们返回相同的哈希/校验和/指纹。

背景:

我正在尝试提出一个简单的模式来检查实体对象自构建以来是否已更改。(为了知道数据库中需要更新哪些对象(。

请注意,我特别不想在我的 setter 或其他任何地方将对象标记为已更改。

我正在考虑以下模式:简而言之,每个应该持久化的实体对象都有一个成员函数"bool is_changed(("。在此上下文中,"已更改"表示自调用对象的构造函数以来已更改。

注意:我这样做的动机是避免将对象标记为干净/脏或按成员比较进行成员比较时附带的样板代码。换句话说,降低人为错误的风险。

(警告:前面是 psudo c++ 代码。我还没有尝试编译它(。

class Foo {
private:
std::string my_string;
// Assume the "fingerprint" is of type long.
long original_fingerprint;
long current_fingerprint()
{
// *** Suggestions on which algorithm to use here? ***
}
public:
Foo(const std::string& my_string) :
my_string(my_string)
{
original_fingerprint = current_fingerprint();
}
bool is_changed() const 
{
// If new calculation of fingerprint is different from the one
// calculated in the constructor, then the object has  
// been changed in some way.
return current_fingerprint() != original_fingerprint;
}
void set_my_string(const std::string& new_string)
{
my_string = new_string;
}
}

void client_code()
{
auto foo = Foo("Initial string");
// should now return **false** because 
// the object has not yet been changed:
foo.is_changed();
foo.set_my_string("Changed string");
// should now return **true** because
// the object has been changed:
foo.is_changed();
}

(*( 在实践中,不一定在理论上(就像 uuid 在理论上不是唯一的(。

这样的函数不存在,至少在您请求的上下文中不存在。

STL 为基本类型 (std::hash( 提供哈希函数,您可以使用这些函数通过任何合理的哈希算法为对象实现哈希函数。

但是,您似乎正在寻找一个注入函数,这会导致问题。本质上,要有一个单射函数,必须有一个大于或等于你正在考虑的对象大小的输出,因为否则(根据鸽子洞原理(将有两个输入给出相同的输出。鉴于此,最明智的选择是直接将对象与某种参考对象进行比较。

您可以使用 Boost 的 CRC32 算法。向它提供要校验和的数据的内存位置。为此可以使用哈希,但哈希是加密函数,旨在防止故意数据损坏,速度较慢。CRC表现更好。

对于此示例,我已将另一个数据成员添加到Foo

int my_integer;

这就是您将my_stringmy_integer校验和的方式:

#include <boost/crc.hpp>
// ...
long current_fingerprint()
{
boost::crc_32_type crc32;
crc32.process_bytes(my_string.data(), my_string.length());
crc32.process_bytes(&my_integer, sizeof(my_integer));
return crc32.checksum();
}

但是,现在我们留下了两个对象具有相同指纹的问题,如果my_stringmy_integer相等。为了解决这个问题,我们应该在CRC中包含对象的地址,因为C++保证不同的对象将具有不同的地址。

有人会认为我们可以使用:

process_bytes(&this, sizeof(this));

这样做,但我们不能,因为this是一个右值,因此我们不能获取它的地址。所以我们需要将地址存储在一个变量中:

long current_fingerprint()
{
boost::crc_32_type crc32;
void* this_ptr = this;
crc32.process_bytes(&this_ptr, sizeof(this_ptr));
crc32.process_bytes(my_string.data(), my_string.length());
crc32.process_bytes(&my_integer, sizeof(my_integer));
return crc32.checksum();
}