Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

shared pointer question

69 views
Skip to first unread message

alexo

unread,
Jun 16, 2019, 11:03:13 AM6/16/19
to
Hello,
trying to play a bit with shared pointers I noticed something I don't
understand.

In my toy code, here integrally reported, everything seems to work
correctly.

My intent is to create a first shared pointer, initialize it, make a
second shared pointer initialize it to make it point to the first
pointer and print the content of the memory location pointed by both to
check if they effectively point to the same block of memory.

cut here:
// ---------------------------------------------------------------------
#include <iostream>
#include <memory>
#include <iomanip>

using namespace std;

void print(const shared_ptr<double[]> & d, size_t n);

int main()
{
size_t n {5};

shared_ptr<double[]> pdata1 {make_unique<double[]>(n)};

auto pdata2 {pdata1};

for(size_t i = 0; i < n; i++)
{
pdata1[i] = static_cast<double> (i) / 2;
}

print(pdata1, n);
print(pdata2, n);

return 0;
}

void print(const shared_ptr<double[]> & d, size_t n)
{
for(size_t i = 0; i < n; i++)
{
cout << showpoint << fixed << setprecision(1) << d[i] << " ";
}

cout << endl;
}

// ---------------------------------------------------------------------

I compiled this code with gcc 7.4.0 using the following options:

-std=c++17 -pedantic -Wall -Wextra

no errors nor warning. Execution shows that the two pointers point to
the same block of memory. But there is a but...

To initialize the first pointer I used the following instruction:

shared_ptr<double[]> pdata1 {make_unique<double[]>(n)};

If I use the function make_shared<> instead, which should be the correct
one, I obtain many error messages, as you can see trying to substitute
the former instruction with the following:

shared_ptr<double[]> pdata1 {make_shared<double[]>(n)};


If instead of an array of doubles I use a single double value...


cut here
//-----------------------------------------------------------------------
#include <iostream>
#include <memory>
#include <iomanip>

using namespace std;

int main()
{
shared_ptr<double> pdata1 {make_shared<double>(2.0)};

auto pdata2 {pdata1};

cout << *pdata1 << endl;
cout << *pdata2 << endl;

return 0;
}

...everything is ok!

What could it be the problem with the double[] type?


Thank you for your patience and kind attention,

alessandro

Melzzzzz

unread,
Jun 16, 2019, 11:25:19 AM6/16/19
to
Probably shared_pointer can't point to array.
>
>
> Thank you for your patience and kind attention,
>
> alessandro


--
press any key to continue or any other to quit...
U ničemu ja ne uživam kao u svom statusu INVALIDA -- Zli Zec
Na divljem zapadu i nije bilo tako puno nasilja, upravo zato jer su svi
bili naoruzani. -- Mladen Gogala

alexo

unread,
Jun 16, 2019, 3:07:41 PM6/16/19
to
Il 16/06/19 17:25, Melzzzzz ha scritto:
It seems to me a little strange, but this is my opinion.

Is there a way to check if the memory is released in the array version?

Melzzzzz

unread,
Jun 16, 2019, 4:29:38 PM6/16/19
to
It is not properly released...

Öö Tiib

unread,
Jun 17, 2019, 4:28:02 AM6/17/19
to
On Sunday, 16 June 2019 18:25:19 UTC+3, Melzzzzz wrote:
> On 2019-06-16, alexo <ale...@inwind.it> wrote:
>
...

> > -std=c++17 -pedantic -Wall -Wextra

...

> > What could it be the problem with the double[] type?
>
> Probably shared_pointer can't point to array.

The shared_ptr works for raw arrays sort of finely but
make_shared does not. The C++20 might (is planned to)
add array support of make_shared. One should read
documentation on preliminary/experimental support
of C++20 of their compiler to figure out extent of
that and since it can be buggy like usually I would
not allow it into product code.

I would (even after C++20) to continue using std::vector or
std::array for all arrays (depending if the size of array is
dynamic or fixed). The shared_ptr is for to achieve shared
ownership of pointed at object (that can indeed be std::vector
or std::array, but that is orthogonal then).

Shared ownership is something that is in actual reality
very rarely needed complication. Masses of shared_ptr in
code are (as kind of rule) preliminary pessimizations and
over-engineerings from novice programmers.

Alf P. Steinbach

unread,
Jun 17, 2019, 6:19:29 AM6/17/19
to
It's a necessary evil.

As I recall, the Boost folks chose shared_ptr as a one-size-fits-all
universal potato thing, rather than the complications of something like
Andrei's policy-based customizable smart pointers.

Strong typing will always involve a conflict between ideal restrictions,
and the mass of complexity a programmer can be expected to know about
and deal with in order to use a near ideal fit abstraction in each case.

Oh, that reminds me, it's about the same issue right now with newspaper
pay-walls in Norway. Nearly all the little local newspapers in Norway
now place nearly all interesting stuff behind pay-walls. As if each
assumes that people all over the country will pay /them/. The cost each
time that you're interested in something is reasonable, sort of. But not
the accumulated cost when you take that kind of cost every day, say.

The result of that high total cost, the accumulated cost, is that
everybody suffer from reduced traffic and hence, reduced ads revenue.
And in development it's the same: too high cost in terms of programmer
education and thinking/design time (plus belatedly realizing that one
chose a slightly wrong path, so necessary to undo efforts and do again),
well it doesn't help, even if in each concrete little case the cost of
the relevant abstraction, in learning and application and runtime
overhead etc., is negligible compared to the savings in that case.
Hence a cheap all-round potato solution is desirable. std::shared_ptr.


Cheers!,

- Alf

alexo

unread,
Jun 17, 2019, 8:40:54 AM6/17/19
to
Il 17/06/19 10:27, Öö Tiib ha scritto:


> The shared_ptr works for raw arrays sort of finely but
> make_shared does not. The C++20 might (is planned to)
> add array support of make_shared. One should read
> documentation on preliminary/experimental support
> of C++20 of their compiler to figure out extent of
> that and since it can be buggy like usually I would
> not allow it into product code.

I'm at a tutorial level of programming in C++. I Self teached
C++ 2003 after having played a bit with C. But I'm just a hobbyst
programmer.

I just wanted to try the code found in the text book:

Beginning C++17 5th edition
by Ivor Horton and Peter Van Weert
Apress

make_unique<> is reported as working with a continuous block of
dynamically allocated memory. Mine code was just an attempt written for
symmetry. The text doesn't mention the same possibility with shared_ptr<>.

if you change the line 13:

shared_ptr<double[]> pdata1 {make_shared<double[]>(n)}

with the following:

shared_ptr<double[]> pdata1 {new double[n]};

the code compiles fine.

Does the memory shared by the two pointer
objects pdata1 and pdata2 get released automatically even if I don't
make use of make_shared? It should be so but I ask to people more
experts in programming than me.

Thank you

Öö Tiib

unread,
Jun 17, 2019, 11:12:58 AM6/17/19
to
On Monday, 17 June 2019 15:40:54 UTC+3, alexo wrote:
> Il 17/06/19 10:27, Öö Tiib ha scritto:
>
>
> > The shared_ptr works for raw arrays sort of finely but
> > make_shared does not. The C++20 might (is planned to)
> > add array support of make_shared. One should read
> > documentation on preliminary/experimental support
> > of C++20 of their compiler to figure out extent of
> > that and since it can be buggy like usually I would
> > not allow it into product code.
>
> I'm at a tutorial level of programming in C++. I Self teached
> C++ 2003 after having played a bit with C. But I'm just a hobbyst
> programmer.
>
> I just wanted to try the code found in the text book:
>
> Beginning C++17 5th edition
> by Ivor Horton and Peter Van Weert
> Apress
>
> make_unique<> is reported as working with a continuous block of
> dynamically allocated memory. Mine code was just an attempt written for
> symmetry. The text doesn't mention the same possibility with shared_ptr<>.
>
> if you change the line 13:
>
> shared_ptr<double[]> pdata1 {make_shared<double[]>(n)}
>
> with the following:
>
> shared_ptr<double[]> pdata1 {new double[n]};
>
> the code compiles fine.

Yes. That (AFAIK since C++17) should work fine.

> Does the memory shared by the two pointer
> objects pdata1 and pdata2 get released automatically even if I don't
> make use of make_shared? It should be so but I ask to people more
> experts in programming than me.

I'm bit confused since I see nothing named "pdata2".
There is difference between usage of make_shared and new
exactly in that life-time and memory management.

With new the managed object is allocated before calling of
constructor and then manager block is allocated separately.
Because of that with new the object can be deleted (IOW
destructor and deallocator combined called) when shared
count reaches zero while the memory for manager block can
be released when also weak count reaches zero.

With make_shared there is however only one allocation (as
optimization) for both managed object and manager block.
Therefore the destructors (but not deallocators) should
be called when shared count reaches zero and the memory for
both should be released when also weak count reaches zero.
That wasn't implemented for arrays in C++17 but likely will
be in C++20.

However I still would avoid using raw array and use std::vector
or std::array instead. It typically wins in robustness and
simplicity and often even in performance.

alexo

unread,
Jun 17, 2019, 12:14:48 PM6/17/19
to
Il 17/06/19 17:12, Öö Tiib ha scritto:
>> if you change the line 13:
>>
>> shared_ptr<double[]> pdata1 {make_shared<double[]>(n)}
>>
>> with the following:
>>
>> shared_ptr<double[]> pdata1 {new double[n]};
>>
>> the code compiles fine.
>
> Yes. That (AFAIK since C++17) should work fine.

OK

>> Does the memory shared by the two pointer
>> objects pdata1 and pdata2 get released automatically even if I don't
>> make use of make_shared? It should be so but I ask to people more
>> experts in programming than me.
>
> I'm bit confused since I see nothing named "pdata2".
> There is difference between usage of make_shared and new
> exactly in that life-time and memory management.

I was referencing to the source code I wrote in a precedent post.
Look at the beginning of this
> However I still would avoid using raw array and use std::vector
> or std::array instead. It typically wins in robustness and
> simplicity and often even in performance.

Understood.

Juha Nieminen

unread,
Jun 18, 2019, 5:13:56 AM6/18/19
to
Öö Tiib <oot...@hot.ee> wrote:
> However I still would avoid using raw array and use std::vector
> or std::array instead. It typically wins in robustness and
> simplicity and often even in performance.

The thing is, shared_ptr represents, well, a shared resource.
Meaning that more than one object can have ownership of the exact
same resource, without having to make copies of it, and without
having to worry which one of them is the last one to own it.
Oftentimes (perhaps most usually) this feature is not needed in
practical code, but sometimes it is.

This cannot really be achieved with either std::vector or std::array.
You can't have several instances of either one point to the same data.
The only options are either copying the data for each object that needs
it (which can be expensive, and removes the option of them actually
functionally sharing the data, eg. to see each other's modifications
of it), or passing references/pointers around, which introduces the
problem of "who is the last one to reference this data?" The original
needs to exist for as long as any object references it.

Of course if the data is owned by one object only, and any other code
only needs it temporarily (eg. only for the duration of one function
call), the std::vector or std::array are probably the better choices.

Lately I have myself starting thinking, however, that for things like
temporary buffers and other "lightweight" dynamic arrays that do not
need any of the functionality that std::vector provides, nor whose
size can be determined at compile time, whether it would be better
to use an std::unique_ptr<Type[]> instead of std::vector<Type>.
The former can be slightly more efficient if Type is a primitive type,
and you don't burden your code with everything that std::vector
provides. (Of course this is at the cost of everything that it does
provide, such as its various assign() functions, size(), empty(),
and so on and so forth.)

Öö Tiib

unread,
Jun 20, 2019, 11:55:30 AM6/20/19
to
On Tuesday, 18 June 2019 12:13:56 UTC+3, Juha Nieminen wrote:
> Öö Tiib <oot...@hot.ee> wrote:
> > However I still would avoid using raw array and use std::vector
> > or std::array instead. It typically wins in robustness and
> > simplicity and often even in performance.
>
> The thing is, shared_ptr represents, well, a shared resource.
> Meaning that more than one object can have ownership of the exact
> same resource, without having to make copies of it, and without
> having to worry which one of them is the last one to own it.
> Oftentimes (perhaps most usually) this feature is not needed in
> practical code, but sometimes it is.
>
> This cannot really be achieved with either std::vector or std::array.

Indeed. But that does not mean we need shared_ptr to raw array
either. We can use shared_ptr to std::array or std::vector like we
use just plain std::array or std::vector when we need non-shared
array.

> You can't have several instances of either one point to the same data.
> The only options are either copying the data for each object that needs
> it (which can be expensive, and removes the option of them actually
> functionally sharing the data, eg. to see each other's modifications
> of it), or passing references/pointers around, which introduces the
> problem of "who is the last one to reference this data?" The original
> needs to exist for as long as any object references it.

I did not try to imply that I can somehow have shared ownership of
data without having reference counting. I meant that it is orthogonal
to choice of object data type. "Object" here is raw array versus
std::vector or std::array.

> Of course if the data is owned by one object only, and any other code
> only needs it temporarily (eg. only for the duration of one function
> call), the std::vector or std::array are probably the better choices.

Those are usually better choice than raw array. An array being
managed by shared_ptr for shared ownership (that is rarely needed)
does not make raw array suddenly better choice.

> Lately I have myself starting thinking, however, that for things like
> temporary buffers and other "lightweight" dynamic arrays that do not
> need any of the functionality that std::vector provides, nor whose
> size can be determined at compile time, whether it would be better
> to use an std::unique_ptr<Type[]> instead of std::vector<Type>.
> The former can be slightly more efficient if Type is a primitive type,
> and you don't burden your code with everything that std::vector
> provides. (Of course this is at the cost of everything that it does
> provide, such as its various assign() functions, size(), empty(),
> and so on and so forth.)

On the ultra rare cases (when that matters) we may want to take a
step back and to investigate why we are allocating/releasing arrays
in such very busy loop and/or we may want to take a step forward
and to consider non-standard things like alloca() or VLA for array.
On common cases it does not matter. There unique_ptr to raw
array adds complications without observable benefits.


Juha Nieminen

unread,
Jun 24, 2019, 7:28:50 AM6/24/19
to
Öö Tiib <oot...@hot.ee> wrote:
> Indeed. But that does not mean we need shared_ptr to raw array
> either. We can use shared_ptr to std::array or std::vector like we
> use just plain std::array or std::vector when we need non-shared
> array.

The size of an std::array needs to be known at compile time. I think it's
probably very rare to need a shared pointer to a compile-time array that's
being nevertheless being allocated dynamically at runtime. I suppose it's
not inconceivable, but rare.

As for allocating an std::vector dynamically, and having a shared pointer
to it... Unless you *really* need the functions provided by the class,
why would you want this double indirection? If all you need is just an
array of values and pretty much nothing else from it (other than being
able to index it), why add needless overhead by using a dynamically
allocated std::vector?

>> Lately I have myself starting thinking, however, that for things like
>> temporary buffers and other "lightweight" dynamic arrays that do not
>> need any of the functionality that std::vector provides, nor whose
>> size can be determined at compile time, whether it would be better
>> to use an std::unique_ptr<Type[]> instead of std::vector<Type>.
>> The former can be slightly more efficient if Type is a primitive type,
>> and you don't burden your code with everything that std::vector
>> provides. (Of course this is at the cost of everything that it does
>> provide, such as its various assign() functions, size(), empty(),
>> and so on and so forth.)
>
> On the ultra rare cases (when that matters)

Not so ultra rare. If you are eg. reading data from a file using
std::fread(), for instance, you often need a temporary array to
read into. If you are reading the *entire* file into the array,
the size of that array is only known at runtime.

In this (not so "ultra rare") case std::unique_ptr<[]> might be
a slightly more lightweight solution than std::vector (especially
since in this case you are probably using a primitive type, and you
probably don't need the array initialized because you are filling it
with data anyway), with little to no drawbacks, if you don't need
anything that std::vector provides.

> we may want to take a
> step back and to investigate why we are allocating/releasing arrays
> in such very busy loop and/or we may want to take a step forward
> and to consider non-standard things like alloca() or VLA for array.

Besides it being non-standard, you'll be burdening the stack with
a potentially very large amount of data. It would be bad if you
run out of stack space.

Öö Tiib

unread,
Jun 25, 2019, 12:46:18 PM6/25/19
to
On Monday, 24 June 2019 14:28:50 UTC+3, Juha Nieminen wrote:
> Öö Tiib <oot...@hot.ee> wrote:
> > Indeed. But that does not mean we need shared_ptr to raw array
> > either. We can use shared_ptr to std::array or std::vector like we
> > use just plain std::array or std::vector when we need non-shared
> > array.
>
> The size of an std::array needs to be known at compile time. I think it's
> probably very rare to need a shared pointer to a compile-time array that's
> being nevertheless being allocated dynamically at runtime. I suppose it's
> not inconceivable, but rare.

That likelihood perhaps depends on problem domain / domain of knowledge
for what we write applications. In domains for what I have written the upper
limits are often clear and shared ownership of virtually unbounded arrays
feels unusual to extreme.

> As for allocating an std::vector dynamically, and having a shared pointer
> to it... Unless you *really* need the functions provided by the class,
> why would you want this double indirection? If all you need is just an
> array of values and pretty much nothing else from it (other than being
> able to index it), why add needless overhead by using a dynamically
> allocated std::vector?

Because it would simplify life with (practically unbounded?) shared array.
If it is potentially major, multi-megabyte array there then I would likely
make std::vector<Element> to be data member of separate class and
so what we talk about here is std::shared_ptr<ThatSeparateClass> and
not shared_ptr<Element[]>.

> >> Lately I have myself starting thinking, however, that for things like
> >> temporary buffers and other "lightweight" dynamic arrays that do not
> >> need any of the functionality that std::vector provides, nor whose
> >> size can be determined at compile time, whether it would be better
> >> to use an std::unique_ptr<Type[]> instead of std::vector<Type>.
> >> The former can be slightly more efficient if Type is a primitive type,
> >> and you don't burden your code with everything that std::vector
> >> provides. (Of course this is at the cost of everything that it does
> >> provide, such as its various assign() functions, size(), empty(),
> >> and so on and so forth.)
> >
> > On the ultra rare cases (when that matters)
>
> Not so ultra rare. If you are eg. reading data from a file using
> std::fread(), for instance, you often need a temporary array to
> read into. If you are reading the *entire* file into the array,
> the size of that array is only known at runtime.

The particular use-case (unless I misunderstood) is sequential
std::fread() from potentially large file or from unknown length
stream?

My experience is that reading in 4096 byte chunks is close to
optimal default (so length does not need to be dynamic) and
same buffer can be reused for whole file/stream (so also its
allocation does not need to be so dynamic). IOW std::array is
usually splendid for that. The run-time data structure into what
the file is read can be anything but usually has finer
organization than (potentially) large immutable scoped
arrays.

> In this (not so "ultra rare") case std::unique_ptr<[]> might be
> a slightly more lightweight solution than std::vector (especially
> since in this case you are probably using a primitive type, and you
> probably don't need the array initialized because you are filling it
> with data anyway), with little to no drawbacks, if you don't need
> anything that std::vector provides.

My starting point is one, 4-8 KB std::array (+ one integer to
indicate how "full" it is) for sequentially reading one file.
It typically remains that regardless if it is small or multi-megabyte
file. On ~5% of cases when that is not performant enough I go full
way to best what is available (like mmap) and for managing that
it is better to have special class (not to hack unique_ptr<char[]>
somehow). OTOH if we talk about hundreds of megabytes
of data then there are database engines for that.

> > we may want to take a
> > step back and to investigate why we are allocating/releasing arrays
> > in such very busy loop and/or we may want to take a step forward
> > and to consider non-standard things like alloca() or VLA for array.
>
> Besides it being non-standard, you'll be burdening the stack with
> a potentially very large amount of data. It would be bad if you
> run out of stack space.

We were talking about allocations/deallocations in so tight sequence
that it alters performance. It means there are lot of relatively small
(not virtually unbounded) buffers involved? Is it is uncertain for
software designer if what the program is dealing with is small or
potentially huge data?

Take projectiles for analogy. Buckshot cartridges are better for
small game and centre-fire cartridges are better for heavy game
but for a tank we need mortar shells. "Silver bullets" that are optimal
for all cases do not exist, so if it is potentially rabbit but potentially
also tank then we perhaps have to carry all three weapons and
choose dynamically what we shoot it with. Now there is also bow
with what small game is hard to hit but heavy game is dangerous
to wound and is hopeless against tank.

Same with software: small std::arrays (possibly in stack) are better
for small data and medium data is better in containers like
std::unordered_map but for large data we likely need database
engine (that deals with memory mapping and indexing internally).
I can imagine how software can pick dynamically between those
three choices but std::unique_ptr<x[]> feels like a bow there. ;)
0 new messages