【问题标题】:How to overload the arrow dereference operator->() not for solid objects but for pointers如何重载箭头取消引用运算符->()不是针对实体对象而是针对指针
【发布时间】:2017-09-07 13:22:32
【问题描述】:

我知道如何为实体(堆栈)对象(如智能指针)重载 operator->

#include <iostream>
struct A { void foo() {} };
struct B {
    A* pa;
    A* operator->() {
        return pa;
    }
};
int main() {
    B b;
    b->foo();  // b is SOLID object on stack: b.pa = ...
}

但我想要这个:

B * pb = new B();
pb->foo(); // to call my overload

数据库管理器单例设计需要这个:

class DatabaseManager
{
private:
    static DatabaseManager * sharedInstance_;
    sqdb::Db *db = nullptr;

public:
    static DatabaseManager * instance();
    sqdb::Db * operator->() {
        return db;
    }
}

我基本上想要这种行为

auto m = DatabaseManager::instance();
m->Query(...); // db->Query 

【问题讨论】:

  • 应该是(*pb)-&gt;foo();。你不能为指针重载operator -&gt;
  • 你不能。没有内置运算符的重载。
  • 你不能这样做。你为什么想要?
  • 为什么不返回参考?
  • @barney 你应该阅读指针/引用。引用在离开作用域时不会导致调用析构函数。此外,析构函数永远不会为新的对象自动调用,这就是为什么它们在现代 C++ 中通常不受欢迎(因为您需要记住手动调用 delete)。

标签: c++ operator-overloading operators


【解决方案1】:

您不能替换指针上的-&gt;

您可以创建重载-&gt; 的智能指针1,但不能重载内置运算符。

auto m = DatabaseManager::instance();
m->Query(...); // db->Query 

这没问题,但是m 的类型不是指针。这将是一些 structclass 与重载的 operator-&gt;

这样的类可能只是unique_ptrshared_ptr 的一个薄包装,例如-&gt; 可以返回&amp;(ptr.get()-&gt;db)

最愚蠢的方法是

class DatabaseManager {
  struct DatabaseManagerPtr {
    DatabaseManager* ptr;
    sqdb::Db * operator->() {
      return ptr?ptr->db:nullptr;
    }
  };
  friend struct DatabaseManagerPtr;
private:
  static DatabaseManager * sharedInstance_;
  sqdb::Db *db = nullptr;

public:
  static DatabaseManagerPtr instance() {
    return {sharedInstance_};
  }
};

这使您的“我希望它工作”代码完全按原样工作,但还有很多更聪明的方法可以做到这一点(可能需要对您的“我希望这个工作”代码进行微调)


1 在 C++ 中,智能指针是任何行为有点像指针的类型,但在某种程度上比原始指针“更智能”,而且以合理的方式。也许它只是穿着更高级的西装; C++ 在将事物称为“智能”指针方面并不是很精英。

【讨论】:

  • 谢谢,这很有启发性。但是为什么我们不能为类型指针重载运算符呢?任何类型化的指针不只是 void* 它具有其类型的信息,因此当我们将 -> 应用于指向某些结构的指针时,编译器使用此“Type*”作为信息来重载想要的操作员不知何故?
  • @barney 因为标准说你不能。该标准说你不能,因为委员会和语言设计者认为它不应该。我相信委员会决定你不能重载内置运算符,因为他们希望内置运算符的语义是固定的并且能够被程序员理解。在 C++ 中最接近重载内置运算符的方法是为 enum 添加重载,在没有这种重载的情况下,它会隐式转换为整数类型,然后内置运算符就可以工作了。没有技术障碍;它被设计禁止。
  • 当然,这正是我试图理解的:他们做出决定的原因。他们通过规则强制执行的使用语义。顺便说一句,这里的主要问题是心理学——我直觉地(错误地)假设类型指针是运算符重载的对象。反之亦然 - 当“取消引用字段访问”运算符实际上应用于实体对象并且没有取消引用传入对象时(但取消引用在最后应用 - 此运算符返回的指向对象的指针) .心灵弯曲!)但仍然适用于具有明显“。”的智能指针。和 -> 调用
【解决方案2】:

规则是:

如果T::operator-&gt;() 存在并且运算符被重载解析机制选择为最佳匹配函数,则表达式x-&gt;m 被解释为x 类型为T 的类对象(x.operator-&gt;())-&gt;m

当 operator-> 返回时,将 operator-> 应用于值 返回,带有原来的第二个操作数。

太容易理解了:只有 .operator-&gt;() 的链式调用可以简化为单个 -&gt;,然后指针的以下取消引用也将“减少”。

所以在这种情况下只需将instance函数更改为返回DatabaseManager&amp;

【讨论】:

  • 所以,最终的 ->m 无论如何都会取消引用从嵌套调用返回的任何内容 - 在应用 m 之前?它应该是一个指针,对吧?
  • 仅假设它之前是.operator-&gt;() 之前的整个链。这对于开头的指针来说是不可能的。
猜你喜欢
  • 2020-08-17
  • 2020-10-14
  • 1970-01-01
  • 1970-01-01
  • 2012-09-01
  • 2018-10-12
相关资源
最近更新 更多