Использовать уникальный ptr для владения и необработанного указателя в противном случае?

Я C++11-ing некоторый код. У меня

class X { /* */ };

class A {
    std::vector<X*> va_x;
};

class B {
    std::vector<X*> vb_x;
    std::vector<A> vb_a;
};

X * s "va_x" внутри моего класса A указывают на объекты, на которые также указывают X*s "vb_x" внутри моего класса B.

Теперь я хотел бы использовать умные указатели. Для меня кажется ясным, что класс B имеет право собственности на объекты, на которые указывает X* (в частности, потому, что мои экземпляры A принадлежат B)

поэтому я должен использовать unique_ptr для X внутри B:

class B {
    std::vector<unique_ptr<X>> vb_x;
    std::vector<A> vb_a;
};

мой вопрос в том, что я должен сделать для класса? Должен ли я держать необработанные указатели? Делая это, в моих модульных тестах я должен признать, что это приводит к неудобным вещам (imo), например (не беспокойтесь о инкапсуляции, это не важно):

unique_ptr<X> x(new X());
A a;
a.va_x.push_back(&(*x)); //awkward, but what else can I do?

A.vb_a.push_back(a); //ok
B.vb_x.push_back(move(x)); //ok

2 ответов


можно использовать x.get(), который возвращает внутренний указатель.

кроме этого, да, использование необработанных указателей для обработки не владеющих ссылками-это путь, см. Также этот вопрос.


как говорит Xeo в своем ответе, решение, как правило, использовать x.get().

есть пример FILE использует x.get() всякий раз, когда он должен быть доступен:

void file_deleter(FILE *f)
{
    fclose(f);
}

[...]

{
    std::unique_ptr<FILE, decltype(&file_deleter)>
                       f(fopen("/tmp/test.tmp", "rw"), &file_deleter);

    // read/write use f.get() as in:
    fread(buf, 1, sizeof(buf), f.get());
    fwrite(buf, 1, sizeof(buf), f.get());

    // fclose() gets called in the deleter, no need to do anything
}

однако в вашем случае вам нужно использовать x.release().

A a;
{
    unique_ptr<X> x(new X());
    a.va_x.push_back(&(*x)); // this is wrong
}
// here a[0] is a dangling pointer

на &(*x) получит необработанный указатель и сделает копию в a вектор. Однако в то время unique_ptr<> выходит за рамки, этот указатель удаляется. Итак, после } указатель в a вектор больше не хорош (хотя он может работать некоторое время.)

правильный способ передачи голого указателя-использовать release() функции как:

    a.va_x.push_back(x.release()); // this works

после этой одной строки указатель находится только в a вектор. Он был освобожден от уникального указателя с идеей, что вызывающий теперь отвечает за управление этим ресурсом.

ВАЖНОЕ ПРИМЕЧАНИЕ: на push_back() может бросить (bad_alloc) и если это произойдет, ресурс будет потерян. Чтобы избежать этой проблемы (в случае, если ваше программное обеспечение ловит bad_alloc и продолжает работать) вам нужно сначала зарезервировать место в векторе, как в:

    a.va_x.reserve(a.va_x.size() + 1);  // malloc() happens here
    a.va_x.push_back(x.release());      // no `bad_alloc` possible here

таким образом,bad_alloc произойдет в этом заявлении, пока ресурс все еще прикреплен к unique_ptr и вы не пропускаете его в случае исключения.

все это сказало, что вы, вероятно, нуждались в . Их можно скопировать без проблем. Ля unique_ptr больше для ресурса, выделяемого один раз, а затем забытого функция возвращает или объект удаляется. Когда (много) копий задействованы, a shared_ptr больше смысла.

class X
{
    typedef std::shared_ptr<X> pointer_t;
    [...]
}

class A
{
    std::vector<X::pointer_t> va_x;
}

X::pointer_t x(new X());
A a;
a.va_x.push_back(x); // much cleaner and the pointer is still managed