-1
#include <iostream>

struct Data
{
    std::shared_ptr<char[]> m_name = nullptr;  // char* m_name = nullptr;

//    bool m_owner = false;

    Data() = default;

    Data(const char* name)
    {
        using std::copy;

        int size = strlen(name) + 1;
        m_name = std::make_unique<char[]>(size); // m_name = new char[size];
        copy(name, name + size, m_name.get());
//        m_owner = true;
    }

//    ~Data()
//    {
//        if (/*m_name &&*/ m_owner)
//            delete[] m_name;
//        m_name = nullptr;
//    }

    void print()
    {
        using std::cout, std::endl;

        if (!m_name)
            return;
        int size = strlen(m_name.get());
        for(int i = 0; i < size; ++i)
            cout << m_name[i];
        cout << endl;
    }
};

void shallow_copy(Data& dst, Data& src)
{
    dst.m_name = src.m_name;
}

void deep_copy(Data& dst, Data& src)
{
    using std::copy;

    int size = strlen(src.m_name.get())+1;
    dst.m_name = std::make_unique<char[]>(size); // dst.m_name = new char[size];
    copy(src.m_name.get(), src.m_name.get() + size, dst.m_name.get());
//    dst.m_owner = true;
}

int main()
{
    using std::cout, std::endl;

    cout << "starting..." << endl;

    auto data1 = new Data{"abc"};
    data1->print();

    auto data2 = new Data();
    data2->print();

//    shallow_copy(*data2, *data1);
    deep_copy(*data2, *data1);

//    delete data1;

    data2->print();

}

While trying to replace char* by std::shared_ptr<char[]> above, I noticed things get a little more verbose with copy, going from

copy(src.m_name, src.m_name + size, dst.m_name);

to

copy(src.m_name.get(), src.m_name.get() + size, dst.m_name.get());

Is there a better (shorter) way than typing that .get() every time?

KcFnMi
  • 5,516
  • 10
  • 62
  • 136
  • 1
    Use std::string if you want more convenient class for things like names. – Öö Tiib Feb 11 '23 at 15:10
  • 1
    Sure. I'm intentionally avoiding it here though, just trying to learn other things. – KcFnMi Feb 11 '23 at 15:11
  • I try to avoid using smart pointers with array types. `std::vector` and `std::string` have always been easier for unique ownership. For shared ownership, even though there's extra memory overhead, I might still prefer a `std::shared_ptr`. I always forget the special notation required for smart pointers to arrays, and it's not worth the headache. – JohnFilleau Feb 11 '23 at 15:18
  • Note `Data{"abc"}` is not allowed in C++ because [you can't convert a string literal to a non-const `char*`](https://stackoverflow.com/questions/20944784/why-is-conversion-from-string-constant-to-char-valid-in-c-but-invalid-in-c) – Ranoiaetep Feb 11 '23 at 15:26
  • Changed to `Data(const char* name)` which seems fine https://godbolt.org/z/f4K9TG6js – KcFnMi Feb 11 '23 at 17:57
  • 2
    `m_name` is declared as `std::shared_ptr`, so why are you allocating its memory with `std::make_unique()` instead of `std::make_shared()`? – Remy Lebeau Feb 11 '23 at 20:06
  • I tried `std::make_shared(size);` and got `No matching function for call to make_shared`. Not sure why, what should I do? – KcFnMi Feb 12 '23 at 07:52

2 Answers2

2

Is there a better (shorter) way than typing that .get() every time?

Not when using smart pointers, no. Best you can do is call .get() earlier and save the pointer for reuse, eg:

char *ptr = src.m_name.get();
copy(ptr, ptr + size, dst.m_name.get());

Alternatively, in this particular example, you can use std::copy_n() instead of std::copy(), eg:

copy_n(src.m_name.get(), size, dst.m_name.get());
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
0

If you know the std::shared/unique_ptr<char[]> won't go out of scope immediately, you can grab a the underlying pointer beforehand and use that. It makes the lines where you use the address look a little neater at the cost of an extra line or two. I personally think it's worth the cost.

const char* const src_ptr = src.m_name.get();
char* const dst_ptr = dst.m_name.get();
// assume m_name for either stays in scope until the next line completes
std::copy(src_ptr, src_ptr + size, dst_ptr);
JohnFilleau
  • 4,045
  • 1
  • 15
  • 22
  • 1
    "going one step back" what does this mean? Non-owning raw pointers are perfectly fine to use in ISO-compliant C++. – JohnFilleau Feb 11 '23 at 15:44