Общий указатель из get() внутри блока

#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 все понял правильно.