Understanding C++ std::shared_ptr when used with temporary objects

自古美人都是妖i 提交于 2019-12-12 17:19:06

问题


I am trying to understand the use of a shared_ptr p when it is used in the construction of an unnamed shared_ptr and the effects this has on p. I was toying with my own examples and wrote the following piece of code:

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. Is it correct that line 5, uses p to create a temp. shared_ptr (i.e an unnamed shared_ptr)?
  2. If so why isn't the use_count increased. Is the temp.object destroyed even before we exit the block at line 7.
  3. If it is destroyed and p's use count becomes zero inside the block, how come it is 1 again after we exit the block?

If I would have used a named shared_ptr q on line 5, i.e:

shared_ptr<int>q(p);

Everything would work as expected, inside the block after line 5 the use count would be 2 and after we exit the block it would be 1 again.


回答1:


According to the C++ Standard (8.5.1.3 Explicit type conversion (functional notation))

1 A simple-type-specifier (10.1.7.2) or typename-specifier (17.7) followed by a parenthesized optional expressionlist or by a braced-init-list (the initializer) constructs a value of the specified type given the initializer...

So the expression in this expression statement

shared_ptr<int>(p);

looks like an explicit type conversion (functional) expression.

On the other hand, a declarator in a declaration can be enclosed in parentheses. For example

int ( x );

is a valid declaration.

So this statement

shared_ptr<int>(p);

can be interpretated as a declaration like

shared_ptr<int> ( p );

So there is an abiguity.

The C++ Standard resolves this ambiguity the following way (9.8 Ambiguity resolution)

1 There is an ambiguity in the grammar involving expression-statements and declarations: An expression statement with a function-style explicit type conversion (8.5.1.3) as its leftmost subexpression can be indistinguishable from a declaration where the first declarator starts with a (. In those cases the statement is a declaration.

Thus this statement in the inner code block

shared_ptr<int>(p);

is a declaration of a new shared pointer with the name p that hides the previous declaration of the object with the same name p in the outer code block and that is created by using the defalut constructor

constexpr shared_ptr() noexcept;

According to the description of this constructor

2 Effects: Constructs an empty shared_ptr object.

3 Postconditions: use_count() == 0 && get() == nullptr.

If you want to deal with an expression instead of the declaration then all you need to do is to enclose the body of the statement in parentheses getting a primary expression in an expression statement.

Here is a demonstrative program.

#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;
}

In this case its output is

#1: 1
#2: 1
#3: 1
#4: 1



回答2:


  1. no

In line 5 you create new variable p. Empty one. See this:

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';

output:

1
address 0x7ffcf3841860
1
address 0x7ffcf3841870
0
1

Note, that address of p has changed.

To fix it change parenthesis:

shared_ptr<int> {p};



回答3:


shared_ptr<int>(p); is equivalent to shared_ptr<int> p;, essentially creating another p inside that block that hides the previous p. The parentheses here are not a constructor call, but are interpreted by the compiler like mathematical parentheses grouping an expression, the expression being the name of the newly constructed shared_ptr.

shared_ptr<int>q(p); instead creates a new shared_ptr named q, calling the constructor with a reference to p as parameter (thus increasing the ref count). The parentheses in this case are interpreted as enclosing the constructor argument.

Note that when you use curly brackets {}, std::shared_ptr<int>q{p}; will continue to give the expected result (1 1 2 1), while std::shared_ptr<int>{p}; will print (1 1 1 1), because the compiler now considers the second p part of the tiny block surrounding it. The joy of programming in C++.



来源:https://stackoverflow.com/questions/56652083/understanding-c-stdshared-ptr-when-used-with-temporary-objects

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!