Как можно реализовать конструктор копирования std :: auto_ptr? - PullRequest
6 голосов
/ 23 декабря 2010

Вернемся к моему сумасшедшему Автоархив штучки ... (цитируя важные биты оттуда:

class AutoArray
{
    void * buffer;
public:
    //Creates a new empty AutoArray
    AutoArray();
    //std::auto_ptr copy semantics
    AutoArray(AutoArray&); //Note it can't be const because the "other" reference
                           //is null'd on copy...
    AutoArray& operator=(AutoArray);
    ~AutoArray();
    //Nothrow swap
    // Note: At the moment this method is not thread safe.
    void Swap(AutoArray&);
};

)

В любом случае, пытаюсь реализовать конструктор копирования. Есть фрагмент клиентского кода (еще не зафиксированный в bitbucket, потому что он не будет собран), который выглядит следующим образом:

AutoArray NtQuerySystemInformation(...) { ... };

AutoArray systemInfoBuffer = NtQuerySystemInformation(...);

Это терпит неудачу, потому что конструктор копирования принимает не-const ссылку в качестве аргумента .... но я не вижу, как вы могли бы изменить конструктор копирования для получения ссылки const, учитывая, что источник AutoArray используемый в назначении изменен (и поэтому не будет const). Конечно, вы не можете изменять вещи так, чтобы они передавались по значению, потому что это конструктор копирования, и это был бы бесконечный цикл!

Если бы я использовал auto_ptr, это было бы правильно:

std::auto_ptr NtQuerySystemInformation(...) { ... };

std::auto_ptr systemInfoBuffer = NtQuerySystemInformation(...);

Как тогда может быть возможен класс с семантикой копирования auto_ptr?

Ответы [ 3 ]

14 голосов
/ 23 декабря 2010

auto_ptr использует подвох.

Я буду использовать скрытый класс с именем auto_int, чтобы продемонстрировать только функциональность создания копии, не внося никаких сложностей, связанных с шаблонами или наследованием. Я думаю, что код в основном правильный, но он не проверен. Наши базовые auto_int выглядят примерно так:

class auto_int
{
public:

    auto_int(int* p = 0) : p_(p) { }

    ~auto_int() { delete p_; }

    // copy constructor taking a non-const reference:
    auto_int(auto_int& other) 
        : p_(other.release()) { }

    int* release() 
    {
        int* temp = p_;
        p_ = 0;
        return temp;
    }

private:

    int* p_;
};

С помощью этого базового auto_int мы не можем скопировать временный объект. Наша цель - написать что-то вроде:

auto_int p(auto_int(new int()));

Что мы можем сделать, это использовать вспомогательный класс. Для auto_ptr это называется auto_ptr_ref. Мы позвоним нашим auto_int_ref:

class auto_int;

class auto_int_ref 
{
public:
    auto_int_ref(auto_int* p) : p_(p) { }

    auto_int& ref() { return *p_; }

private:
    auto_int* p_;
};

По сути, экземпляр этого класса просто хранит указатель на auto_int и позволяет нам использовать его как «ссылку» на auto_int.

Тогда в нашем auto_int классе нам понадобятся две дополнительные функции. Нам нужен другой конструктор, который принимает auto_int_ref, и нам нужен оператор преобразования, который позволяет неявно преобразовывать auto_int в auto_int_ref:

auto_int(auto_int_ref other)
    : p_(other.ref().release()) { }

operator auto_int_ref() { return this; }

Это позволит нам «копировать» временный файл, в то время как конструктор копирования принимает неконстантную ссылку. Если мы снова посмотрим на наш пример кода:

auto_int p(auto_int(new int()));

Что происходит, мы создаем новый временный auto_int и передаем new int() в конструктор, который принимает int*. Затем это временное значение преобразуется в auto_int_ref, который указывает на него, с использованием operator auto_int_ref(), а конструктор auto_int, который принимает auto_int_ref, используется для инициализации p.

3 голосов
/ 23 декабря 2010

auto_ptr копия ctor работает , забирая право владения от переданного объекта. Это также большая часть причины, по которой auto_ptr нельзя использовать в vector или других коллекциях STL.

Это не так, как работает большинство конструкторов копирования. Обычно ваш конструктор копирования просто клонирует переданный объект, поэтому вы можете передать на него константную ссылку. Но auto_ptr этого не делает. Это фактически изменило оригинальный объект. В этом смысле это только конструктор копирования по имени, а не по семантике.

EDIT2:

Я пытаюсь это немного свести. Так эффективно вы пытаетесь сделать что-то с вашим классом, что позволит синтаксис, подобный этому:

#include <string>
#include <memory>
using namespace std;

auto_ptr<string> gimme()
{
    return auto_ptr<string>(new string("Hello"));
}

int main()
{
    auto_ptr<string> s = gimme();
}

... и вам интересно, как заставить работать s = gimme() часть. Верно?

Секрет здесь заключается в прокси-классе auto_ptr_ref, описанном Стандартом в 20.4.5, цель которого - «разрешить передачу объектов auto_ptr и возвращение их из функций»:

namespace std {
  template <class Y> struct auto_ptr_ref {};
  template<class X> class auto_ptr {
  public:
    typedef X element_type;

    // 20.4.5.1 construct/copy/destroy:
    explicit auto_ptr(X* p =0) throw();
    auto_ptr(auto_ptr&) throw();
    template<class Y> auto_ptr(auto_ptr<Y>&) throw();
    auto_ptr& operator=(auto_ptr&) throw();
    template<class Y> auto_ptr& operator=(auto_ptr<Y>&) throw();
    auto_ptr& operator=(auto_ptr_ref<X> r) throw();
    ˜auto_ptr() throw();
    // 20.4.5.2 members:
    X& operator*() const throw();
    X* operator->() const throw();
    X* get() const throw();
    X* release() throw();
    void reset(X* p =0) throw();
    // 20.4.5.3 conversions:
    auto_ptr(auto_ptr_ref<X>) throw();
    template<class Y> operator auto_ptr_ref<Y>() throw();
    template<class Y> operator auto_ptr<Y>() throw();
  };
}
0 голосов
/ 23 декабря 2010

Нет неявного преобразования из T* в std::auto_ptr<T>.Я предполагаю, что у вас есть неявный конструктор преобразования из дескриптора NTSTATUS в AutoArray.Но если это преобразование создает временное, это временное копирование невозможно.

Если вы используете прямую инициализацию, а не копирование, ваша «проблема» может уйти.

AutoArray systemInfoBuffer( ntDll.NtQuerySystemInformation(
  Dll::NtDll::SystemProcessInformation) );
...