Trivial: the declaration is not complete - you're missing the final ; after the class definition.
Missing includes:
#include <algorithm>
#include <cstddef>
using std::size_t; // don't do this in a header!
As noted in the comment, don't put standard library identifiers into the global namespace - just write the type in full throughout the interface.
Don't use a null pointer for an empty string. That's going to result in a lot of special-case code when you implement the rest of the String interface (e.g. operator==(), operator+=, and more).
In this constructor:
MyString(const char* cstr):
pSize{compute_length(cstr)}, pStr{nullptr}
{
pStr = new char[pCapacity];
std::copy(cstr, cstr + pSize, pStr);
}
As well as using an array which may be too small, we're assigning to pStr twice. We could do it just once, if we rearrange the order of the data members:
private:
static constexpr std::size_t default_capacity = 14;
std::size_t pSize;
std::size_t pCapacity = default_capacity;
char* pStr;
public:
MyString(const char* cstr)
: pSize{compute_length(cstr)},
pCapacity{pSize},
pStr{new char[pCapacity]}
{
std::copy_n(cstr, pSize, pStr);
}
The same problem exists with the copy constructor, and is as easily fixed:
MyString(const MyString& rhs):
pSize{rhs.pSize},
pCapacity{pSize},
pStr{new char[pCapacity]}
{
std::copy_n(rhs.pStr, pSize, pStr);
}
Actually, I'd re-write all the constructors both in terms of a new (const char*, std::size_t) constructor:
public:
MyString(const char* s, std::size_t len)
: pSize{len},
pCapacity{len},
pStr{new char[len]}
{
std::copy_n(s, len, pStr);
}
MyString()
: MyString{""}
{}
MyString(const char* cstr)
: MyString{cstr, std::strlen(cstr)}
{}
MyString(const MyString& rhs)
: MyString{rhs.pStr, rhs.pSize}
{}
Well done for remembering the test for self-assignment here:
MyString& operator=(const MyString& rhs){
if(this == &rhs)
return *this;
I'm not convinced it's a good idea to retain the string's storage when it's already large enough to accommodate rhs - this means that any string that's ever held a very long string will continue to use that space until it's destroyed. That's not what most users expect when they write s = ""! Even worse, we copy that huge capacity to any other string we construct or assign from this:
MyString s = some_huge_string(); // capacity is now big
s = ""; // capacity is still big
MyString s1 = s; // s1's capacity is also big, even though it's an empty string!
MyString s2{""}; // s2's capacity is small
s2 = s; // now it's huge, even though its content is unchanged
Perhaps use a threshold to determine whether the storage should be reduced? I'd go with something like
if (rhs.pSize > pCapacity
|| rhs.pSize < pCapacity / 2 && rhs.pSize < default_capacity)
And much of the duplication in this function can be reduced:
MyString& operator=(const MyString& rhs){
if (this == &rhs) {
return *this;
}
if (rhs.pSize > pCapacity
|| rhs.pSize < pCapacity / 2 && rhs.pSize < default_capacity)
{
char *new_str = new char[rhs.pSize];
delete[] pStr;
pStr = new_str;
pCapacity = rhs.pSize;
}
pSize = rhs.size();
std::copy_n(rhs.pStr, pSize, pStr);
return *this;
}
You'll need to write a move-constructor and move-assignment soon - I recommend writing a swap() first, and implementing those in terms of swap().
Modified code
As there were no tests, I added a simple main() to exercise the class.
#include <algorithm>
#include <cstddef>
#include <cstring>
class MyString {
public:
MyString(const char* s, std::size_t len)
: pSize{len},
pCapacity{len},
pStr{new char[len]}
{
std::copy_n(s, len, pStr);
}
MyString()
: MyString{""}
{}
MyString(const char* cstr)
: MyString{cstr, std::strlen(cstr)}
{}
MyString(const MyString& rhs)
: MyString{rhs.pStr, rhs.pSize}
{}
~MyString()
{
delete[] pStr;
}
std::size_t size() const { return pSize; }
std::size_t capacity() const { return pCapacity; }
const char* data() const { return pStr; }
MyString& operator=(const MyString& rhs)
{
if (this == &rhs) {
return *this;
}
if (rhs.pSize > pCapacity
|| rhs.pSize < pCapacity / 2 && rhs.pSize < default_capacity)
{
char *new_str = new char[rhs.pSize];
delete[] pStr;
pStr = new_str;
pCapacity = rhs.pSize;
}
pSize = rhs.size();
std::copy_n(rhs.pStr, pSize, pStr);
return *this;
}
private:
static constexpr std::size_t default_capacity = 14;
std::size_t pSize;
std::size_t pCapacity = default_capacity;
char* pStr;
};
#include <iostream>
int main()
{
MyString s0 = "abc";
MyString s1 = s0;
s1 = "";
s1 = s0;
std::cout.write(s1.data(), s1.size());
std::cout << '\n';
}