Авто Указатель конструктора в VC2008 - PullRequest
3 голосов
/ 14 апреля 2009

У меня есть автоматическая реализация указателя:

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 * () переменной temp, а затем конструктор GAutoPtr (T * ptr = 0) вместо простого использования конструктора копирования: GAutoPtr (AutoPtrRef p)

Это приводит к тому, что temp auto ptr удаляет память, а 'a' держит указатель на освобожденную память.

Однако в VC6 он вызывает правильный конструктор. Говоря обо всем этом, я также использую gcc для Linux и Mac, поэтому любой код, который я пишу, должен работать и там. VC2008 запрещает использование неконстантной переменной по значению в конструкторе копирования. Кроме того, я не хочу, чтобы "const" в любом случае, потому что конструктор копирования становится владельцем блока памяти, который удаляет владение копируемого объекта ... изменяя его.

Как я могу сделать это в VC 2005/2008?

Ответы [ 2 ]

1 голос
/ 14 апреля 2009

Это даже компилировалось под недавним 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, что я и ожидал. Похоже, Херб Саттер написал о чем-то очень похожем на это в своем последнем GotW. Я бы не стал копировать семантику передачи права собственности std::auto_ptr без очень веской причины. Возможно, вы захотите взглянуть на различные вкусности в Boost.SmartPtr . Если возможно, используйте их вместо этого.

Если по какой-либо причине вы не можете повысить, прочитайте вашу любимую реализацию std::auto_ptr и обратите особое внимание на класс std::auto_ptr_ref. Как только вы поймете, почему он существует и как именно он делает то, что он делает, вернитесь назад и напишите класс auto-ptr. Этот класс существует, чтобы обойти проблему, которую вы видите. IIRC, это подробно обсуждается в Josuttis ': Стандартная библиотека C ++ . Я думаю, что именно там я впервые понял это.

1 голос
/ 14 апреля 2009

Вы можете пометить конструктор, который принимает аргумент T *, с помощью «явного» ключевого слова. Я убедился, что это работает и предотвращает создание ложного временного объекта, что повышает производительность. Однако любой, кто использует этот конструктор, больше не может полагаться на правила преобразования типов компилятора. Например, ваша функция изменится на это:

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

Это раздражает, но я не думаю, что это обязательно плохо в этом случае, поскольку автоматическое преобразование типов может сбивать с толку.

...