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

Я C ++ 11-й код. я имею

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

8

Решение

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

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

10

Другие решения

Как говорит 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 и вы не пропустите его в случае исключения.

Все это говорит, что вы, вероятно, нуждались в shared_ptr вместо. Те могут быть скопированы без проблем. unique_ptr больше для ресурса, выделяемого один раз, а затем забытого, который возвращает функция или объект удаляется. Когда (много) копий, 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
-1

По вопросам рекламы [email protected]