Понимание C ++ std :: shared_ptr при использовании с временными объектами - PullRequest
3 голосов
/ 18 июня 2019

Я пытаюсь понять, как использовать shared_ptr p, когда он используется в конструкции неназванного shared_ptr и как это влияет на p. Я играл с моими собственными примерами и написал следующий фрагмент кода:

shared_ptr<int> p(new int(42));
cout << p.use_count() << '\n';          
{ 
  cout << p.use_count() << '\n';
  shared_ptr<int>(p);
  cout << p.use_count() << '\n';
}
cout << p.use_count() << '\n';

Output:
1
1
0
1
  1. Правильно ли, что строка 5 использует p для создания темп. shared_ptr (т.е. неназванный shared_ptr)?
  2. Если так, то почему use_count не увеличился. Уничтожен ли временный объект прежде чем мы выйдем из блока в строке 7.
  3. Если он уничтожен и счетчик использования p становится нулем внутри блока, почему это снова 1 после выхода из блока?

Если бы я использовал имя shared_ptr q в строке 5, то есть:

shared_ptr<int>q(p);

Все будет работать как положено, внутри блока после строки 5 значение счетчика будет равно 2, и после выхода из блока оно снова будет равно 1.

Ответы [ 3 ]

3 голосов
/ 18 июня 2019

Согласно стандарту C ++ (8.5.1.3 Явное преобразование типов (функциональная запись))

1 Спецификатор простого типа (10.1.7.2) или спецификатор типа (17.7) за которым следует необязательный список выражений в скобках или Braced-init-list (инициализатор) создает значение указанного введите данный инициализатор ...

Итак, выражение в этом выражении оператора

shared_ptr<int>(p);

выглядит как явное (функциональное) выражение преобразования типа.

С другой стороны, декларатор в объявлении может быть заключен в скобки. Например

int ( x );

является действительным объявлением.

Итак, это утверждение

shared_ptr<int>(p);

можно интерпретировать как объявление типа

shared_ptr<int> ( p );

Итак, есть какая-то неясность.

Стандарт C ++ разрешает эту неоднозначность следующим образом (9.8 Разрешение неоднозначности)

1 В грамматике существует двусмысленность, связанная с выражениями-выражениями и объявления: оператор выражения с функциональным стилем явное преобразование типов (8.5.1.3), поскольку его крайнее левое подвыражение может быть неотличимым от декларации, где первый декларатор начинается с (. В этих случаях оператором является объявление .

Таким образом, этот оператор во внутреннем кодовом блоке

shared_ptr<int>(p);

- это объявление нового общего указателя с именем p, которое скрывает предыдущее объявление объекта с тем же именем p во внешнем кодовом блоке и создается с помощью конструктора defalut

constexpr shared_ptr() noexcept;

Согласно описанию этого конструктора

2 Эффекта: создание пустого объекта shared_ptr.

3 Постусловия: use_count () == 0 && get () == nullptr.

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

Вот демонстрационная программа.

#include <iostream>
#include <memory>

int main() 
{
    std::shared_ptr<int> p( new int( 42 ) );

    std::cout << "#1: " << p.use_count() << '\n';          

    { 
        std::cout << "#2: " << p.use_count() << '\n';

        ( std::shared_ptr<int>( p ) );

        std::cout << "#3: " << p.use_count() << '\n';
    }

    std::cout << "#4: " << p.use_count() << '\n';

    return 0;
}

В этом случае его вывод равен

#1: 1
#2: 1
#3: 1
#4: 1
2 голосов
/ 18 июня 2019
  1. нет

В строке 5 вы создаете новую переменную p. Пустой Смотрите это:

shared_ptr<int> p(new int(42));
cout << p.use_count() << '\n';
cout << "address " << &p << "\n";
{
    cout << p.use_count() << '\n';
    shared_ptr<int>(p);
    cout << "address " << &p << "\n";
    cout << p.use_count() << '\n';
}
cout << p.use_count() << '\n';

выход:

1
address 0x7ffcf3841860
1
address 0x7ffcf3841870
0
1

Обратите внимание, что адрес p изменился.

Чтобы исправить это, измените скобки:

shared_ptr<int> {p};
1 голос
/ 18 июня 2019

shared_ptr<int>(p); эквивалентно shared_ptr<int> p;, по сути создавая еще один p внутри этого блока, который скрывает предыдущий p. Скобки здесь не являются вызовом конструктора, но интерпретируются компилятором как математические скобки, сгруппирующие выражение, причем выражение является именем вновь созданного shared_ptr.

shared_ptr<int>q(p); вместо этого создает новый shared_ptr с именем q, вызывающий конструктор со ссылкой на p в качестве параметра (таким образом увеличивая количество ссылок). Скобки в этом случае интерпретируются как заключающие аргумент конструктора.

Обратите внимание, что при использовании фигурных скобок {}, std::shared_ptr<int>q{p}; продолжит давать ожидаемый результат (1 1 2 1), в то время как std::shared_ptr<int>{p}; выведет (1 1 1 1), потому что компилятор теперь учитывает вторая p часть крошечного блока, окружающего его. Радость программирования на С ++.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...