3

我有一个自动指针实现:

template <typename T, bool Arr = false>
class GAutoPtr
{
    T *Ptr;

public:
    typedef GAutoPtr<T, Arr> &AutoPtrRef;

    GAutoPtr(T *ptr = 0)
    {
        Ptr = ptr;
    }

    GAutoPtr(AutoPtrRef p)
    {
        Ptr = p.Release();
    }

    ~GAutoPtr() { Empty(); }
    operator T*() { return Ptr; }
    T *Get() { return Ptr; }
    T *operator->() const { LgiAssert(Ptr); return Ptr; }

    inline void Empty()
    {
        if (Arr)
            delete [] Ptr;
        else
            delete Ptr;
        Ptr = 0;
    }

    AutoPtrRef operator =(GAutoPtr<T> p)
    {
        Empty();
        Ptr = p.Ptr;
        p.Ptr = 0;
        return *this;
    }

    void Reset(T *p)
    {
        if (p != Ptr)
        {
            Empty();
            Ptr = p;
        }
    }

    T *Release()
    {
        T *p = Ptr;
        Ptr = 0;
        return p;
    }
};

typedef GAutoPtr<char, true> GAutoString;
typedef GAutoPtr<char16, true> GAutoWString;

这在 Visual C++ 6 中运行良好。但是在 Visual C++ 2005 或 2008 中,我无法从函数返回自动指针,而不会出现严重错误。

例如

GAutoString Func()
{
    char *s = new char[4];
    strcpy(s, "asd");
    return s;
}

int main()
{
    GAutoString a = Func();
    /// a.Ptr is now garbage
}

发生的情况是编译器创建一个临时 GAutoString 来保存函数的返回值,然后在将它传递给堆栈上的变量“a”时调用临时变量的运算符 T*(),然后调用 GAutoPtr( T *ptr = 0) 构造函数,而不是仅仅使用复制构造函数:GAutoPtr(AutoPtrRef p)

这会导致 temp auto ptr 删除内存,并且 'a' 持有指向已释放内存的指针。

但是在 VC6 中,它确实调用了正确的构造函数。现在说这一切,我也在 Linux 和 Mac 上使用 gcc,所以我写的任何代码也需要在那里工作。VC2008 阻止您在复制构造函数中使用非常量值变量。另外我也不想要“const”,因为复制构造函数获取内存块的所有权,这会从被复制的对象中删除所有权......从而修改它。

如何在 VC 2005/2008 中完成这项工作?

4

2 回答 2

1

您可以使用“显式”关键字标记采用 T* 参数的构造函数。我已经验证这可以工作并且也可以防止创建虚假临时对象,从而提高性能。不过,任何使用该构造函数的人都不能再依赖编译器类型转换规则。例如,您的功能将更改为:

GAutoString Func()
{
    char *s = new char[4];
    strcpy(s, "asd");
    return GAutoString(s);
}

这很烦人,但我认为在这种情况下这不一定是一件坏事,因为自动类型转换可能会令人困惑。

于 2009-04-14T01:20:28.110 回答
1

这甚至是在最近的 g++ 下编译的吗?我在我的 MacBook Pro 上试了一下:

xxx.cpp: In function ‘AutoString func()’:
xxx.cpp:52: error: no matching function for call to ‘AutoPtr<char, true>::AutoPtr(AutoString)’
xxx.cpp:12: note: candidates are: AutoPtr<T, isArray>::AutoPtr(AutoPtr<T, isArray>&) [with T = char, bool isArray = true]
xxx.cpp:9: note:                 AutoPtr<T, isArray>::AutoPtr(T*) [with T = char, bool isArray = true]
xxx.cpp:52: error:   initializing temporary from result of ‘AutoPtr<T, isArray>::AutoPtr(T*) [with T = char, bool isArray = true]’

我可以编译它的唯一方法是让复制构造函数获取const我所怀疑的引用。看起来Herb Sutter在他最近的 GotW 中发布了与此非常相似的内容。std::auto_ptr如果没有很好的理由,我不会尝试复制所有权转移语义。您可能想看看Boost.SmartPtr中的各种好东西。如果可能,请改用它们。

如果您出于某种原因无法 Boost,请阅读您最喜欢的std::auto_ptr实现并特别注意该std::auto_ptr_ref课程。一旦你理解了它为什么存在以及它是如何做的,然后回去编写一个 auto-ptr 类。此类的存在是为了解决您所看到的问题。IIRC,这在Josuttis': The Standard C++ Library中有详细讨论。我想这是我第一次真正理解它的地方。

于 2009-04-14T02:52:26.597 回答