#c 11
#c 11
Вопрос:
У меня возникла некоторая путаница со следующим фрагментом кода
#include <iostream>
#include <memory>
using namespace std;
int main()
{
int *iptr = new int(12); //create built-in pointer
shared_ptr<int> s(iptr); //create shared pointer to it
int *q = s.get(); //get built-in back using get (redundant step, but I was practicing)
shared_ptr<int> (q); //Does NOT work without giving it a separate block ; error: conflicting declaration ‘std::shared_ptr q’
//{shared_ptr<int> (q);} //This works!!
return 0;
}
Мой вопрос в том, почему один работает, а другой нет? Какое имя получает указатель в ограниченном блоке? похоже, у него нет имени, если мы будем следовать следующему синтаксису [ type name(args)
]
Ответ №1:
Проблема здесь на самом деле не в общих указателях, вы просто неправильно интерпретировали часть своего собственного кода. Строка:
shared_ptr<int> (q);
это не конструкция анонима, shared_ptr<int>
использующая q
в качестве аргумента конструктора. На самом деле это объявление имени shared_ptr<int>
. Вы действительно написали:
shared_ptr<int> q;
который, очевидно, не будет компилироваться, поскольку вы используете имя q
уже в том же блоке. Закомментированный код будет скомпилирован, поскольку это другой блок, и внутренний q
будет затенять внешний q
.
Также:
- Вам следует избегать использования
new
напрямую. Используйтеstd::make_shared<int>()
вместо этого. - Никогда не передавайте указатель на память, у которой есть другой владелец, конструктору
std::shared_ptr<>
, поскольку этот конструктор становится владельцем памяти и предполагает, что может ее использовать, даже если эта память была освобождена первоначальным владельцем.
Комментарии:
1. Да, я не заметил, что имя переменной отсутствует. 1
2. так что это похоже
Type name
(отсюда и объявление). В этом случае это был быnull
указатель и фактически не принимал бы значениеiptr
. Но в книге (C Primer) предполагается, что у него будет адрес и, следовательно, будет удалена память, на которую он ссылается, когда блок заканчивается. Откуда он получаетarg
? PS: Я пытаюсь добавить здесь подробности, но это не позволяет мне добавлять новые строки. Извините!
Ответ №2:
Я сбился с пути, @einpoklum все понял правильно.
Вы не можете создать больше такого указателя shared_ptr
из одного и того же простого указателя. Все остальные shared_ptr
ы должны быть копиями другого shared_ptr
.
Это потому, что shared_ptr
необходимо выделить другой объект, который поддерживает счетчик ссылок для вашего объекта, и shared_ptr
конструктор из простого указателя делает это. shared_ptr
конструкторы копирования просто увеличивают этот счетчик ссылок.
Когда вы создаете несколько shared_ptr
объектов из одного и того же простого указателя, они выделяют свои собственные счетчики ссылок и в конечном итоге удаляют ваш объект несколько раз. Что приводит к неопределенному поведению (оно часто завершается сбоем при втором удалении).
Также boost::intrusive_ptr
ожидается, что счетчик ссылок будет находиться внутри объекта. Следовательно, из одного и того же boost::intrusive_ptr
простого указателя можно создать несколько , потому что все они используют один и тот же счетчик ссылок внутри объекта.
boost::intrusive_ptr
также более эффективен, чем shared_ptr
, но он не поддерживает слабые указатели.
boost::intrusive_ptr
более эффективен, потому что он имеет тот же размер, что и обычный указатель ( shared_ptr
размер s вдвое больше этого), потому что ему не нужно выделять отдельную структуру со счетчиком ссылок и объектом deleter в другом месте и сохранять другой указатель на это.
В многопоточном приложении shared_ptr
всегда используется более дорогостоящее атомарное приращение / уменьшение для обслуживания счетчика ссылок, даже если вы никогда не передаете эти указатели по потокам. В хорошо спроектированном приложении только shared_ptr
определенные T
потоки когда-либо передаются по потокам, и только они shared_ptr<T>
должны использовать атомарный счетчик. С boost::intrusive_ptr
вы используете атомарный счетчик только для объектов, которые передаются между потоками, и используете простой целочисленный счетчик для остальных ваших объектов.
Комментарии:
1. Под «не может» вы имеете в виду, что компилятор выдаст ошибку или что этого не следует делать? Также можете ли вы прокомментировать, почему это работает внутри отдельного блока, а не без него? Спасибо!!
2. @User10482 К сожалению, компилятор вам ничего не скажет, когда из одного простого указателя создаются несколько
shared_ptr
файлов.3. @User10482 Я сбился с пути, einpoklum все понял правильно.