37

在我研究 Qt 的源代码时,我看到 trolltech 的家伙明确地使用this关键字来访问析构函数上的字段。

inline ~QScopedPointer()
{
    T *oldD = this->d;
    Cleanup::cleanup(oldD);
    this->d = 0;
}

那么,这种用法有什么意义呢?有什么好处吗?

编辑:对于那些投票支持关闭这个问题的人,我怀疑这种用法是针对某些类继承情况的

QScopedPointer 类定义的一部分:

template <typename T, typename Cleanup = QScopedPointerDeleter<T> >
class QScopedPointer
4

2 回答 2

52

C++ 答案(一般答案)

考虑一个Derived带有模板基类的模板类:

template <typename T>
class Base {
public:
    int d;
};

template <typename T>
class Derived : public Base<T> {
    void f () {
        this->d = 0;
    }
};

this有 type Derived<T>,一个依赖于 的类型T。所以this有一个依赖类型。所以this->dd一个从属名称。从属名称在模板定义的上下文中作为非从属名称和在实例化的上下文中查找。

如果没有this->,则该名称d将仅作为非依赖名称进行查找,而不会被找到。

另一种解决方案是d在模板定义本身中声明:

template <typename T>
class Derived : public Base<T> {
    using Base::d;
    void f () {
        d = 0;
    }
};

Qanswer(具体答案)

d是 的成员QScopedPointer。它不是继承的成员。this->这里没有必要。

OTOH,QScopedArrayPointer是一个模板类,d是模板基类的继承成员:

template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
class QScopedArrayPointer : public QScopedPointer<T, Cleanup>

所以这里this->是必要的:

inline T &operator[](int i)
{
    return this->d[i];
}

很容易看出,this->随处放置更容易。

了解原因

我想所有 C++ 用户都不清楚为什么在非依赖基类中查找名称而不是在依赖基类中查找名称:

class Base0 {
public:
    int nd;
};

template <typename T>
class Derived2 : 
        public Base0, // non-dependent base
        public Base<T> { // dependent base
    void f () {
        nd; // Base0::b
        d; // lookup of "d" finds nothing

        f (this); // lookup of "f" finds nothing
                  // will find "f" later
    }
};

除了“标准这么说”之外还有一个原因:模板中名称绑定方式的原因。

当模板被实例化时,模板可以具有迟绑定的名称:例如ff (this). 在Derived2::f()定义点,编译器不知道变量、函数或类型名称ff在这一点上,可以引用的已知实体集是空的。这不是问题,因为编译器知道它f稍后会作为函数名或模板函数名进行查找。

OTOH,编译器不知道该怎么做d;它不是(称为)函数名。没有办法对非(调用)函数名称进行后期绑定。

现在,所有这些看起来都像是编译时模板多态性的基本知识。真正的问题似乎是:为什么在模板定义时不d绑定?Base<T>::d

真正的问题是Base<T>::d模板定义时没有,因为当时没有完整的类型Base<T>Base<T>已声明,但未定义!你可能会问:这个呢:

template <typename T>
class Base {
public:
    int d;
};

它看起来像一个完整类型的定义!

实际上,在实例化之前,它看起来更像:

template <typename T>
class Base;

给编译器。无法在类模板中查找名称!但仅在模板专业化(实例化)中。模板是制作模板专业化的工厂,模板不是一组模板专业化。编译器可以查找d任何Base<T>特定类型T,但不能查找d类模板Base。在确定类型之前TBase<T>::d仍然是抽象的Base<T>::d;只有当 typeT已知时,才Base<T>::d开始引用 type 的变量int

这样做的结果是类模板 Derived2有一个完整的基类Base0,但有一个不完整的(前向声明的)基类Base。仅对于已知类型T,“模板类”(类模板的特化)Derived2<T>具有完整的基类,就像任何普通类一样。

你现在看到了:

template <typename T>
class Derived : public Base<T> 

实际上是一个基类规范模板(制作基类规范的工厂),它遵循与模板内的基类规范不同的规则。

备注:读者可能已经注意到我在解释的最后编造了几个词组。

这是非常不同的:这里d是一个限定名称Derived<T>,并且Derived<T>是依赖的,因为T它是一个模板参数。一个限定名可以是后期绑定的,即使它不是一个(被调用的)函数名。

另一个解决方案是:

template <typename T>
class Derived : public Base<T> {
    void f () {
        Derived::d = 0; // qualified name
    }
};

这是等价的。

如果您认为在 的定义中Derived<T>Derived<T>有时将其视为已知完整类而有时将其视为未知类不一致,那么您是对的。

于 2011-10-26T20:30:03.093 回答
1

我猜这与 Cleanup() 例程的超载使用有关。传递的类型由模板类型 T 显式控制,而 T 又可以控制调用 Cleanup() 的哪个重载版本。

于 2011-10-26T20:34:42.593 回答