3

As inspired by Demo of shared ptr array

I got the first two lines to work:

std::shared_ptr<string> sp( new string[3], []( string *p ) { delete[] p; } );
*sp = "john";
auto p = &(* sp);
++p = new string("Paul");
++p = new string("Mary");
for(auto q = &(*sp); q <= p; q++) cout << *q << endl;

(1) Can someone show me how to access subsequent elements of my array and print them out with a for loop?

My for loop does not print anything with MSVC V19 and and g++ v4.9 prints "john" but not "Paul" and "Mary" and then gives me a segmentation fault.

Now after some more google/bing searching I found some discussions suggesting I should use unique_ptr if I are not sharing it.

So I got to experimenting some more and this works:

  const int len=3;
  std::unique_ptr<string[]> up( new string[len] ); // this will correctly call delete[]
  up[0] = "john";
  up[1] = "paul";
  up[2] = "mary";
  for(int ii = 0; ii < len; ++ii) cout << up[ii] << endl;

(2) Is there a way to print the array up without hard coding the length in a constant? I was hoping that there was a way to make the new C++ for loop syntax work but it appears only work on std::array and std::vector.

(3) Is there is an easier way to initialize this array? Perhaps with an initializer list?

Community
  • 1
  • 1
user3002625
  • 71
  • 1
  • 3

3 Answers3

2

To access subsequent elements use the shared_ptr::get member function

std::shared_ptr<string> sp( new string[3], std::default_delete<string[]>() );
sp.get()[0] = "John";
sp.get()[1] = "Paul";
sp.get()[2] = "Mary";
for(auto q = sp.get(); q < sp.get() + 3; q++) cout << *q << endl;

The problem with dereferencing the shared_ptr is that it'll return a string&, but you want to get access to the underlying pointer in order to be able to index to next element.

You can also initialize the shared_ptr array upon construction using list initialization

std::shared_ptr<string> sp( new string[3]{"John", "Paul", "Mary"}, 
                            std::default_delete<string[]>() );

As for unique_ptr, as you've noted, there exists a partial specialization that'll delete array types correctly. However, it does not store the length of the array, you'll need to store it separately and pass it around along with the unique_ptr. For one-line initialization you can use the same syntax as above.

std::unique_ptr<string[]> up( new string[len]{"John", "Paul", "Mary"} );

However, neither the shared_ptr nor the unique_ptr can be used with ranged-based for loops. The specification of a range for requires the operand to either be an array, or it must have begin() and end() member functions, or the begin and end iterators for the range must be obtainable by ADL using the expressions begin(__range) and end(__range) respectively. None of these conditions are satisfied by either the shared_ptr or the unique_ptr containing an array.


Unless you have a good reason not to, you should just use std::vector<std::string>, that'll save you the trouble of tracking array length separately. std::array<std::string, N>> is also another option if you know the length of the array at compile time.

Praetorian
  • 100,267
  • 15
  • 224
  • 307
2

With C++17, operator[] is defined for shared_ptr<T[]> (link). Before that it was there for unique_ptr, but not for shared_ptr.

So, the following code will work.

std::shared_ptr<string[]> sp(new string[3]);
    sp[0] = "John";
    sp[1] = "Paul";
    sp[2] = "Mary";

    for(int i =0; i< 3; i++)
    {
        cout<<sp[i]<<"\n";
    }

Note that it is define for shared_ptr<T[]>, and not for shared_ptr<T>, i.e. when you have a dynamically allocated array. Also I have removed your custom deleter, as your's is similar to the default one which the compiler will use, you are however free to add it.

For your second query - being able to use new C++ for loop (range based for loop), you cannot use this smart pointer directly, since range based for loop works only on containers which have begin(), end(), operator++, operator*() , and, operator!= defined (link). The simplest way is to use std::vector with pre-allocated size. eg. vector<T> sp(3), or vector<T> sp; sp.reserve(3). This will work as good as your smart pointers' approach. To get underlying memory you can use vector<T>::data() eg. sp.data(). This will give you a dynamic C array of 3 elements (n elements in general).

Sahil Singh
  • 2,396
  • 29
  • 46
1

How to index into C++ shared_ptr/unique_ptr array?

You've already shown the code for unique_ptr.

This works for shared_ptr.

std::shared_ptr<string> sp( new string[3], []( string *p ) { delete[] p; } );
sp.get()[0] = "string 1";
sp.get()[1] = "string 2";
sp.get()[2] = "string 3";
R Sahu
  • 196,807
  • 13
  • 136
  • 247