首页 > 代码库 > 见微知著——从自定义类型的operator==说起

见微知著——从自定义类型的operator==说起

今天打算用C++模拟一下Java的Object对象。需求很简单,通过一个自定义用户类型包装一个内建类型,并提供equals、hashCode、=和== 4种函数。

源码如下:

#pragma once
#ifndef ENTITY_H_
#define ENTITY_H_
#include <functional>
template<typename T>
class Entity {
private:
    T obj;
    Entity(const Entity&);
public:
    Entity(T t) :obj(t) {};

    bool equals(Entity& other) {
        std::hash<T> tohash;
        return tohash(obj) == other.hashCode();
    }

    size_t hashCode() {
        std::hash<T> tohash;
        return tohash(obj);
    }

    Entity<T>& operator=(const Entity& rv) {
        obj = rv.obj;
        return *this;
    }

    friend bool operator==(const Entity& left, const Entity& right) {
        return left.hashCode() == right.hashCode();
    }
};

#endif // !ENTITY_H_

C++11 提供了Hash方法正好拿来使用,可是在测试==的时候却发现编译器报错:“size_t Entity<int>::hashCode(void)”: 不能将“this”指针从“const Entity<int>”转换为“Entity<int> &”。说实话,平时Java用习惯了,猛然看见C++的报错信息真是一脸懵逼。后来在网上查了一些资料逐渐明白了个中缘由。

首先修改代码:

size_t hashCode() const {
        std::hash<T> tohash;
        return tohash(obj);
    }

测试通过!做法很简单,但是要说明清楚这个问题,我们还得从对象方法与函数的区别谈起。

1. 函数与方法

函数是一个很原始的概念,在古代面向对象的语言还没有诞生。大家为了封装一些公共算法发明了函数这个概念,通常是根据实参经过函数体返回一个计算结果,并且实参要和形参对应。后来,面向对象的语言出现了,大家开始考虑让对象实施某些行为,这就是——方法。本质上,函数叫function和方法叫method。对象方法之于普通函数的最大区别是,它可以直接访问对象内部的成员属性(field)。在Java中我们通过this.field的方式就能够访问,在C++中更普遍的做法是this->field。这里就很奇怪了,为什么在method内部能直接使用this呢?原因是语言在编译过程中,编译器将对象(this)作为参数置入了method中。因此实际上,method的真实形象应该是这样的:method(this, args)。熟悉面向过程语言特性的朋友一定清楚,如果不用面向对象的思想实际上也可以用function模拟method,即也是将对象本身作为参数传递给function。

2. function(args) const

const blob b(2);

这里,b是类型blob的一个const对象。它的构造函数被调用,并且参数为“2”。由于编译器强调对象为const,因此它必须保证对象的数据成员(fields)在其生命周期内不被改变。然而,仅仅是在类声明中给出const还不能保证成员函数按声明的方式去做。所以,编译器会强制程序员在定义函数时重申const。这就是function(args) const的由来。

3. 结论

在明白了以上两点以后,我们再回到本例中。由于operator==(const Left& lv, const Right& rv)在函数中要求提供的两个参数对象都必须是const类型。因此在整个函数体中,这些对象执行的方法也必须是const类型。然而现在我们知道成员函数会隐式的调用对象本身,换句话说hashCode()方法会分别调用Left与Right,而这个方法并没有被重申为const。这就出现了开头看到的那段异常:“size_t Entity<int>::hashCode(void)”: 不能将“this”指针从“const Entity<int>”转换为“Entity<int> &”!

见微知著——从自定义类型的operator==说起