Reputation: 55
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
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
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