重载“==”运算符和与nullptr的比较?

3

我正在编写一个C++/CLI包装器,用于包装一个C++本机静态库。

我对C++/CLI和C++都没有太多经验。我遵循了在网上阅读到的最佳实践来创建C++/CLI包装器。我的包装器具有指向C++类的指针。我的C++类已经重载了运算符==。

我正在尝试在我的包装器中也重载它,并使用C++类的实现,但是当包装器为空时,我遇到了错误。

我搜索了如何知道我的句柄是否为空,并发现你必须将其与nullptr进行比较。

我在C++/CLI中有这个方法

       MyOtherClass const* MyClass::MyMethod(MyWrapper^ instance)   
    {
        if(instance == nullptr)
        {
           return NULL;
        }

        return instance->Property;      
    }

代码中的if(instance == nullptr)调用了我重载的==操作符。

      static bool operator==(MyWrapper^ a, MyWrapper^ b)
    {
        return a->InternalInstance == b->InternalInstance; //Here System.AccessViolationException Exception
    }

问题在于如果 a 为 null,这将会抛出一个 System.AccessViolationException 异常。
我不能简单地添加一个与 nullptr 的比较来检查 ab,因为这会导致堆栈溢出。
static bool operator==(MyWrapper^ a, MyWrapper^ b)
{
    if(a == nullptr && b == nullptr) //Stack Overflow here because a == nullptr calls this method again.
        return true;
    if((a == nullptr && b != nullptr) || (a != nullptr && b == nullptr))
        return false;
    return a->InternalInstance == b->InternalInstance;
}

我怎样可以覆盖==运算符,使用我的C++本地实现,并且确保保护我的句柄不为空?


如果MyWrapper ^不是空指针,您可以有一个显式重载的bool()运算符,它返回true。 - yizzlez
如果(a && b && a->InternalInstance == b->InternalInstance)应该可以实现。如果a或b为NULL,则会计算为false,因此其他条件将永远不会被执行。 - Zac Howland
@ZacHowland 我尝试使用 return a && b && a->internalUnit == b->internalUnit;,但是它不起作用,会抛出对象引用错误。我认为这是因为当我调试句柄时,它是“未定义”的。我猜这与C++的行为不同。 - Dzyann
@Dzyann 这可能是CLI特定的问题,但如果指针为NULLnullptr,则如果您有语句if(a),它应该等同于false。另一个选项是使用ReferenceEquals - Zac Howland
@ZacHowland 你说得对,那正是我所期望的,也是为什么我测试了你的代码,我想知道它实际上会做什么,例如当你执行“a &&…”时,它是否会在内部调用a == NULL?好吧,我不知道它做了什么,但在C++/CLI中,你的建议遗憾地不起作用。我甚至尝试在返回之前的一行中设置a = nullptr,以查看它是否与我没有初始化a有关,但无论如何都失败了。 - Dzyann
2个回答

7

使用Object::ReferenceEquals显式检查null。

static bool operator==(MyWrapper^ a, MyWrapper^ b)
{
    if(Object::ReferenceEquals(a, nullptr) && Object::ReferenceEquals(b, nullptr))
        return true;

    if(Object::ReferenceEquals(a, nullptr) || Object::ReferenceEquals(b, nullptr))
        return false;

    return a->InternalInstance == b->InternalInstance;
}

不需要加上 Object::。它是继承来的。 - Ben Voigt
5
我知道,但我认为在调用静态方法时指定类名是一种通常的良好惯例。 - David Yaw

2

这里的问题在于,在C++/CLI中,重载函数o == operate是在引用类型上执行的,因此如果我们按照相同的方式在本机C++中执行此操作,这意味着==运算符的重载函数是在指针类型上执行的。但是在本机C++中,我们通常会像这样定义==运算符:

bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ }

或者这样想,如果在本地C++中为指针类型过度使用==,那么如何将此类指针与null进行比较?
在托管世界中,默认情况下,运算符==通过确定两个引用是否指示相同对象来测试引用相等性,因此引用类型不需要实现运算符==即可获得此功能。因此,您应该实现Equals函数而不是重载==。
请参考此链接:Guidelines for Overloading Equals() and Operator ==。虽然它是关于C#的,但我认为它也适用于C++/CLI中的托管类型。

所以,我在SO上跟随另一个问题覆盖了运算符。但这没有意义吗?它应该读作bool operator==(const X% lhs, const X% rhs),而不是像我现在这样的bool operator==(const X^ lhs, const X^ rhs)。因为现在你提到了它,我从来没有见过bool operator==(const X* lhs, const X* rhs)。我理解你的意思了吗? - Dzyann
对于像链接中提到的不可变对象,执行operator==(const X%lhs,const X%rhs)是毫无意义的(不是一个好主意),因为框架实现已经进行了引用相等性比较。然而,在我的情况下,我有一个不可变对象,我可以使用David发布的内容,这也建议在您的链接中。非常感谢提供的信息。 - Dzyann

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接