带有显式转换的悬挂指针

Dangling pointer with explicit conversion

在 Scott Meyrses Effective C++ 中,他提供了一个错误使用隐式转换和 RAII 的示例 类:

class Font { // RAII class
public:
    explicit Font(FontHandle fh) // acquire resource;
        : f(fh) // use pass-by-value, because the
    {} // C API does
    ~Font() { releaseFont(f ); } // release resource
    ... // handle copying (see Item14)
private:
    FontHandle f; // the raw font resource
};

以及隐式转换函数:

class Font {
public:
    ...
    operator FontHandle() const // implicit conversion function
    { return f; }
    ...
};

他还提供了这种错误用法的示例:

Font f1(getFont());
...
FontHandle f2 = f1; // oops! meant to copy a Font
                    // object, but instead implicitly
                    // converted f1 into its underlying
                    // FontHandle, then copied that

他是这样说的:

Now the program has a FontHandle being managed by the Font object f1, but the FontHandle is also available for direct use as f2. That’s almost never good. For example, when f1 is destroyed, the font will be released, and f2 will dangle.

为什么f2会悬空?将f1中包含的FontHandle资源复制到f2中后,f2f1将是完全独立的对象。那么,如果我们释放 f1,它将如何影响 f2?我们应用copying,而不是moving.

FontHandle 本身就是一个指针。 f2 是 RAII class 范围之外的指针副本。当 f1 超出范围时,它将释放 f1 中 fonthandle 指向的内存...但 f2 仍然指向它。

"FontHandle" 绰号是为了清晰起见,但很多时候这些 C APIs,为了分离和隐藏实现,将让 API 函数采用类似一个空指针 (void*),只有 API 的内容具有取消引用和使用的信息。然后他们可能会做类似 typedef void *FontHandle 的事情,这样你就知道 void 指针在语义上代表什么。

假设FontHandle是一个指针,那么f2和私有成员f1.f会指向同一个东西。如果 f1 不复存在(例如,超出范围,或被显式释放),则 f2 将成为悬空指针。

注意转换函数:

operator FontHandle() const // implicit conversion function
{ return f; }

每当您需要将 Font 转换为 FontHandle 时,这只是 returns 获取的句柄。 F2 将是这个对象本身,而不是副本。 C++ 希望您在这里自己制作一个副本。此外,

当你

FontHandle f2 = f1; // oops! meant to copy a Font

这调用了上面的运算符。它不调用复制构造函数。为什么?

语句t1 = t2;等同于:

t1.operator=(t2);

由于你上面没有定义,隐式转换,按照你上面的定义。复制构造函数只会在你这样做时被调用

Font f2 = f1;