P. Tarala
P. Tarala

Reputation: 55

How do Implement the copy constructor for my custom c++ class

I am creating a string class and I have set up my default constructor. I want to know how to go about setting up the copy constructor.

I have already tried the default constructor and the c string constructor. I am confused on what I should do next.

DSString::DSString()
{
    data = nullptr;
}

DSString::DSString(const char* d)
{
    data = new char[strlen(d) + 1];
    strcpy(data,d);
}

DSString::DSString(const DSString&)
{
}

I expect the result to be functional. but it is not functional right now.

Upvotes: 2

Views: 2348

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596266

You can do the same kind of initialization in your DSString& copy constructor that you do in your char* converting constructor, simply use the data member of the input DSString as the char* to copy from, eg:

DSString::DSString(const char *d)
{
    // the behavior of strlen() and strcpy() are undefined if d is null...
    if (d)
    {
        data = new char[strlen(d) + 1];
        strcpy(data, d);
    }
    else
        data = nullptr;
}

DSString::DSString(const DSString &src)
{
    // the behavior of strlen() and strcpy() are undefined if src.data is null...
    if (src.data)
    {
        data = new char[strlen(src.data) + 1];
        strcpy(data, src.data);
    }
    else
        data = nullptr;
}

Or, since you are clearly using C++11 or later (by virtue of the use of nullptr), you can simply have your copy constructor delegate to your converting constructor itself to avoid repeating code:

DSString::DSString(const DSString &src)
    : DSString(src.data)
{
}

Don't forget to also include a destructor, a move constructor, a copy assignment operator, and a move assignment operator, per the Rule of 3/5/0:

DSString::DSString(DSString &&src)
    : DSString()
{
    //std::swap(data, src.data);
    data = src.data;
    src.data = nullptr;
}

DSString::~DSString()
{
    delete[] data;
}

DSString& DSString::operator=(const DSString &rhs)
{
    if (&rhs != this)
    {
        DSString tmp(rhs);
        //std::swap(data, tmp.data);
        char *d = data;
        data = tmp.data;
        tmp.data = d;
    }
    return *this;
}

DSString& DSString::operator=(DSString &&rhs)
{
    DSString tmp(std::move(rhs));
    //std::swap(data, tmp.data);
    char *d = data;
    data = tmp.data;
    tmp.data = d;
    return *this;
}

Now, that being said, if you were to use std::string instead of char* for your data member (as you should), you get most of this functionality for free from the compiler, you don't have to implement it manually, except for the char* converting constructor:

class DSString
{
private:
    std::string data;
public:
    DSString() = default;
    DSString(const char *d) : data(d ? d : "") {}
    DSString(const DSString&) = default;
    DSString(DSString&&) = default;
    ~DSString() = default;
    DSString& operator=(const DSString&) = default;
    DSString& operator=(DSString&&) = default;
};

Upvotes: 6

Joseph Larson
Joseph Larson

Reputation: 9058

I started with Remy's version. My version is more NULL-safe and shows how to use initializer lists in your constructors.

DSString::DSString() : data(nullptr)
{
}

DSString::DSString(const char* d) : data(nullptr)
{
    if (d != nullptr) {
        data = new char[strlen(d) + 1];
        strcpy(data,d);
    }
}

DSString::DSString(const DSString &src) : data(nullptr)
{
    if (src.data != nullptr) {
        data = new char[strlen(src.data) + 1];
        strcpy(data, src.data);
    }
}

DSString::~DSString()
{
    if (data != nullptr) {
        delete[] data;
        data = nullptr;
    }
}

DSString & DSString::operator=(const DSString &rhs)
{
    // This if protects against self-assignment.
    if (&rhs != this)
    {
        DSString tmp(rhs);
        std::swap(data, tmp.data);
    }
    return *this;
}

Upvotes: 1

Related Questions