【问题标题】:What is the purpose of a virtual operator ()() here?这里虚拟运算符 ()() 的目的是什么?
【发布时间】:2017-03-16 01:41:09
【问题描述】:

我正在尝试修改 code I found,但由于我不了解此虚拟运算符的目的、重要性和/或相关性而受阻:

  1. 有人可以提供有关此运算符为何必要或有用的见解吗?
  2. 我认为将parentItem()rect_resizer_ 作为参数,然后修改resizer_ 的值是否正确?

.h 中的构造函数:

virtual void operator()(QGraphicsItem* item, const QRectF& rect) = 0;

调用.cpp:

(*resizer_)(parentItem(), rect_);

构造函数的修剪上下文以供参考:

class SizeGripItem : public QGraphicsItem
{
    private:

        class HandleItem : public QGraphicsRectItem
        {
            public:
                HandleItem(int positionFlags, SizeGripItem* parent);

            private:    
                SizeGripItem* parent_;
        };

    public:
        class Resizer
        {
            public:
                virtual void operator()(QGraphicsItem* item,
                                        const QRectF& rect) = 0;
        };

        SizeGripItem(Resizer* resizer = 0, QGraphicsItem* parent = 0);
        virtual ~SizeGripItem();

    private:
        void doResize();
        QRectF rect_;
        Resizer* resizer_;
};

【问题讨论】:

  • 看起来像函数对象。
  • 看起来Resizer 是一种回调方式,以便SizeGripItem 的客户端可以在set_x_ 成员函数之一(调用doResize()) 被调用。

标签: c++ qt c++11 virtual operator-keyword


【解决方案1】:

Resizer 是对polymorphic functor(函数对象)的broken 尝试。这样的成语在 C++11 之前很有用。它被破坏了,因为没有虚拟析构函数,这样的仿函数就毫无用处。它应该如下所示:

class Resizer {
public:
  virtual void operator()(QGraphicsItem* item, const QRectF& rect) = 0;
  virtual ~Resizer() {}
};

这样的对象是可调用的:

void invokeResizer(Resizer * resizer, QGraphicsItem * item, const QRectF & rect) {
  (*resizer)(item, rect);
}

上面会在resizer对象上执行operator()(QGraphicsItem*,const QRectF&)方法。

在现代代码中,应该使用std::function<void(QGraphicsItem*, const QRectF &)>,而不是这样的黑客。

【讨论】:

  • 从技术上讲,如果你没有要破坏的东西,没有虚拟析构函数应该是安全的。
  • @ddriver 该类是多态和抽象的:它意味着被重载。在每一个这样的类中,没有虚拟析构函数是一个错误。如果函子不能保持状态,那么使用函子(而不是函数指针)是没有意义的。由于函子可以保持状态(它是一个对象是有原因的!),它必须有一个虚拟析构函数。没有如果,也没有关于它的问题。在 C++ 中,函子是可以保持状态的可调用对象的习惯用法。如果你想强制一个无状态的可调用,使用一个函数指针:这是一个明确表示“不允许状态”的习语。
  • 理论上这是未定义的行为,但实际上最终发生的是正在调用类型指针的析构函数,这将是基类的析构函数。如果派生没有添加额外的成员并且只是覆盖了功能,那么没有成员将保持未破坏状态。释放内存也不是问题,因为片段大小通常首先存储在片段本身中。重载并不一定意味着添加成员。它可以覆盖功能,或者添加功能,在这两种情况下运行基类而不是派生的析构函数实际上都可以。
  • @ddriver 不,它永远不会好。多态函子是您和您的用户之间的契约。如果不提供虚拟析构函数,您就违反了合同。现在您可能会争辩说,没有析构函数指定函子不意味着有任何数据成员:如果是这样,那么到底为什么要让它成为函子而不是函数指针。所以,是的,这肯定是坏的,而且没有任何理论依据。作为这样一个 API 的用户,我会非常生气。多态函子是一种习惯用法,您应该在设计中正确使用它。
  • @ddriver 可以这样想:C++ 习语与英语等自然语言中的习语没有什么不同。假设有人一直说“雪上加霜”而不是“雪上加霜”。你可能会轻笑,但它很刺耳,而且不正确。这些习语带有预期用途的范围:适用性的上下文。对于任何精通的人来说,多态函子都有一定的意义,一定的期望。这种期望的一个关键部分是它以一种有用的方式是有状态的。否则它将是一个没有void* 参数的无状态函数指针。
【解决方案2】:

关于第 2 点,考虑这一行:

(*resizer_)(parentItem(), rect_);

resizer_ 可能是指向未知类型对象T 的指针,因此*resizer 是对同一类型对象T 的引用。
如果它有一个operator() 的定义,它接受两个类型为decltype(parentItem())decltype(rect_) 的参数,您可以像示例中那样调用它。
换句话说,它相当于:

resizer_->operator()(parentItem(), rect_);

它根本不会修改resizer_ 的值。

有人可以提供有关此运算符为何必要或有用的见解吗?

嗯,这主要取决于上下文和它旨在解决的实际问题。
从一行代码很难说。
如果您觉得它没用,请不要使用它。就是这样。

【讨论】:

  • 是的。我错过了两件事。一,它被声明为纯虚拟,所以有些地方应该在使用前覆盖版本。此外,这些函数对象类型主要用作谓词(就像我们在 STL 函数中使用的那样),但在这里它只是用作普通函数。
  • @PAVANCHANDAKA 纯虚方法是该语言的一个特性。我想不可能给出正确的定义,图书馆设计者选择让用户定义它。另一个问题是有问题的,普通函数可以用作谓词,所以我不明白这个问题。
猜你喜欢
  • 2010-12-30
  • 1970-01-01
  • 2021-01-14
  • 1970-01-01
  • 1970-01-01
  • 2021-03-17
  • 2018-03-13
  • 1970-01-01
相关资源
最近更新 更多