user defined string::reserve() - c++

So we are writing a bunch of user defined functions that mimic the string class member functions. I am stuck on reserve. It looks like it should work but I am missing something. I have tried a couple of diffrent variations such as putting this-> in front of the member varibles or my string:: in front of them... my out put does not change but throught cout statment placed within the function I know it access the function.
here is the code
#include <cstdlib>
#include <iostream>
#include <string>
#include "mystring.h"
#define string mystring
using namespace std;
void check (string s, string name){
cout << "checking " << name << endl;
cout << name << " contains " << s << endl;
cout << name << " capacity() is " << s.capacity() << endl;
cout << name << " length() is " << s.length() << endl;
cout << name << " size() is " << s.size() << endl;
cout << name << " max_size() is " << s.max_size() << endl << endl;
}
int main(int argc, char** argv) {
cout<<"This is Lab 5"<<endl;
string s1("Hello, World!");//step 2
string s1name("s1");//step 3
check(s1,s1name);//step 4
check(s1,s1name);//step 5
cout << "---Testing assignment operator---\n\n";
{
string s2;
s2=s1;
string s2name("s2");
check(s2,s2name);
if(s1==s2)
cout<<"comparison true\n";
else
cout<<"comparison false\n";
}
check(s1,s1name);
string s3("check assignment");
s3=s3;//checking to see if operator= is used when they are the same object.
check(s3,"s3");
cout<<"Lab 5 ends"<<endl;//step6
// //clear check
// s3.clear();
// check(s3,"s3");
// if(s1==s3)
// cout<<"comparison true\n";
// else
// cout<<"comparison false\n";
// reserve check
// mystring::size_type res;
// res=40;
s3.reserve(40);//still working on reserve
check(s3,"s3");
cout<<"in main buf size"<<s3.capacity()<<endl;
s3.reserve(5);
check(s3,"s3");
// char* test=s3.begin();
// cout<<&test<<endl;
// cout<<&s3<<endl;
//empty check
// string s4;
//
// if (s4.empty())
// cout<<"Empty is true\n";
// else
// cout<<"Empty is false\n";
return 0;
}
#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream>
#include <math.h>
#include <cstring>
using namespace std;
class mystring {
public:
// types with scope in this class
typedef unsigned int size_type;
typedef char * iterator;
typedef const char * const_iterator;
static const long int npos = 1073741824;
// default constructor
mystring();//good
// other constructors
mystring(const char *);//good
// copy constructor
mystring(const mystring& orig);//
// destructor
~mystring();////
// iterators
iterator begin();//good
iterator end();//good
//=== memory related ===
// change buffer size to n
void reserve(size_type n);
size_type size() const;////good returns len
size_type length() const;////good returns len
size_type capacity() const;////good returns buf_size
size_type max_size() const;////good
bool empty() const;////good
//=== overloading operators ===
// assignment operator
mystring& operator=(const mystring&);////
// mystring& operator=(const char *);
// array notation
char operator[](size_type pos) const;
char& operator[](size_type pos);
// append
mystring& operator+=(const mystring& str);
mystring& operator+=(const char * str);
//=== methods that modifiy the string
void clear();////good
void push_back(char c);
mystring& append(const mystring& str);
mystring& append(const char * str);
mystring& insert(size_type pos, const mystring& str);
mystring& insert(size_type pos, const char * str);
mystring& replace(size_type start, size_type span, const mystring& str);
mystring& replace(size_type start, size_type span, const char * str);
//=== conversion to c string
const char * c_str() const;//
private:
// pointer to the memory location where string is stored as a c-style
// string
char * ptr_buffer;
// the size of the memory in terms of bytes or characters capable of going into it currently
size_type buf_size;
// number of characters currently in the memory not including the
// terminating null character
size_type len;
};
#include "mystring.h"
// default constructor provided for lab 5
mystring::mystring() {
ptr_buffer = new char[1];
*ptr_buffer = '\0';
buf_size = 1;
len = 0;
}
// constructor from c-style string or "abc" provided for lab 5
mystring::mystring(const char * s) {
len = strlen(s);
buf_size = len + 1;
ptr_buffer = new char[buf_size];
strcpy(ptr_buffer, s);
}
// copy constructor to be implemented in lab 5
mystring::mystring(const mystring& orig) {
len=orig.length();
ptr_buffer=new char[len+1];
buf_size=len+1;
for(int n=0 ;n<buf_size; n++ )
{
ptr_buffer[n]=orig.ptr_buffer[n];
}
ptr_buffer[buf_size]='\0';
}
void mystring::reserve(size_type n)
{
cout<<"cccccc:"<<capacity()<<endl;
if( n > capacity() )
{
const char* temp = ptr_buffer;
ptr_buffer = new char[n];
memcpy(ptr_buffer, temp, len+1);
delete [] temp;
buf_size=n;
cout<<"bbbbbuf size"<<buf_size<<endl;
}
// char *temp;
//
// temp=new char[n];
//
// int i=0;
//
// for(;i<=len;i++)
// {
// temp[i]=ptr_buffer[i];
//
// }
// buf_size=n;
//
// delete [] ptr_buffer;
//
// ptr_buffer=temp;
//
}
mystring::iterator mystring::begin()//think is working correctly
{
iterator it=ptr_buffer;
return it;
}
mystring::iterator mystring::end()//think is working correctly
{
iterator it=ptr_buffer+len;
return it;
}
// one of the over loaded assignment operator to be implemented // assignment 3 (or for lab 5 if you have more time)
mystring& mystring::operator=(const mystring& orig){
if(this!=&orig)
// {
// cout<<"this==&mystring if statment activated\n";//comment out after testing
// break;
// }
{
delete this->ptr_buffer;
this->len=orig.len;//length();
this->ptr_buffer=new char((this->len)+1);
this->buf_size=(this->buf_size)+1;
cout<<"Using assignment operator="<<endl;
for(int n=0;n<this->buf_size;n++)
{
this->ptr_buffer[n]=orig.ptr_buffer[n];
}
this->ptr_buffer[buf_size]='\0';
return *this;
}
}
// some simple methods provided for lab 5
mystring::size_type mystring::size() const {
return len;
}
mystring::size_type mystring::length() const{
return len;
}
mystring::size_type mystring::capacity() const{
return buf_size;
}
mystring::size_type mystring::max_size() const{
return (int)pow(2,30) -4 ;
}
bool mystring::empty() const
{
if(len==0)
return true;
else
return false;
}
// destructor to free space implemented for lab 5
mystring::~mystring() {
delete [] ptr_buffer;
}
// provided for lab 5 so we may cout mystring
ostream& operator<<(ostream& out, const mystring& str) {
out << str.c_str();
return out;
}
// provided for lab 5 to support the implementation of <<
const char * mystring::c_str() const {
return ptr_buffer;
}
char mystring::operator[](size_type pos) const
{
return *(ptr_buffer+pos);
}
void mystring::clear()
{
char *temp;
temp=new char[1];
temp[0]='\0';
ptr_buffer=temp;
buf_size=0;
len=0;
}
void mystring::push_back(char c)
{
}
bool operator==(const mystring& lhs, const mystring& rhs)
{
if(lhs.length()==rhs.length())
{
for(int i=0; i<lhs.length();i++)
{
if(lhs[i]!=rhs[i])
return false;
}
return true;
}
return false;
}
So I put some cout statements in the reserve() function and in main. The reserve functions is changing the buffer size and capacity call is returning correctly but when we use the check function it is not showing that any change has occurred. I have put in revised code.
Ok, everyone. Turns out on the check function declared before main that it makes a copy of the mystring object. The copy constructor was defined to make buf_size= len + 1. So I turned it into buf_size=orig.buf_size. The reserve function probably worked as stated and yes it needed an if statment to make sure we were not shrinking it, but i was not worried about that yet. I used daves example with a little added code. thanks all for your help.

void mystring::reserve(size_type n)
{
if (n <= buf_size)
{
return;
}
from the MSDN basic_string::reserve Sets the capacity of the string to a number at least as great as a specified number.
So reserve can't be used to truncate a string
Here we are doing something stronger (we don't let the "user" shorten the buffer). Cleary you could
if (n <= len + 1) // remember the \0 terminator is not included in len
{
return;
}
and it would be correct.
But even the first form would be correct. There isn't any guarantee that reserve will "reduce" the size of a buffer.
In general, to copy between buffers, you shoud use memcpy with a length of len + 1. It's (normally) faster (no one requires it to be faster, but it is faster :-) )

Related

Concatenate 2 string using operator+= in class C++

I've seen some similar questions before asking, but I'm still stuck at the part of concatenating two strings using operator+=.
Currently, I can get separate strings correctly by constructor method. But when I compile code, the line str[length+i] = s[i]; in the method String& String::operator+= (const String& s) shows an error:
no match for ‘operator[]’ (operand types are ‘const String’ and ‘unsigned int’)
So I need your help to fix this bug.
Here's my code:
#include <iostream>
#include <cstring>
class String {
// Initialise char array
char* data;
unsigned length;
public:
// Constructor without arguments
String();
// Constructor with 1 arguments
String(char* s);
// Copy Constructor
String(const String& source);
// Move Constructor
String(String&& source);
// Destructor
~String() { delete[] data; }
/*!
* #brief String length.
* #return Value in String #c length.
*/
unsigned len ( ) const;
/*!
* #brief Append to String.
* #param[in] s A String object.
* #return A String reference to *this.
* #post String will equal the concatenation of itself with #a s.
*/
String& operator+= (const String& s);
};
// Constructor with no arguments
String::String()
: data{ nullptr }
{
data = new char[1];
data[0] = '\0';
}
// Constructor with one arguments
String::String(char* s)
{
if (s == nullptr) {
data = new char[1];
data[0] = '\0';
}
else {
data = new char[strlen(s) + 1];
// Copy character of s[]
// using strcpy
strcpy(data, s);
data[strlen(s)] = '\0';
std::cout << data << "\n";
}
}
// Copy Constructor
String::String(const String& source)
{
data = new char[strlen(source.data) + 1];
strcpy(data, source.data);
data[strlen(source.data)] = '\0';
}
// Move Constructor
String::String(String&& source)
{
data = source.data;
source.data = nullptr;
}
unsigned String::len ( ) const
{
return length;
}
String& String::operator+= (const String& s)
{
unsigned len = length + s.len();
char* str = new char[len];
for (unsigned j=0; j < length; j++)
str[j] = data[j];
for (unsigned i=0; i < s.len(); i++)
str[length+i] = s[i];
delete data;
length = len;
data = str;
return *this;
}
int main()
{
// Constructor with no arguments
String a;
// Convert string literal to
// char array
char temp[] = "Hello world.";
// Constructor with one argument
std::cout << "s1: ";
String s1{ temp };
// Copy constructor
String s11{ a };
char temp1[] = "Goodbye!";
std::cout << "s2: ";
String s2{ temp1 };
String s3 = String s1 + String s2;
return 0;
}
Another way of writing main function:
int main()
{
String s1("Hello World.");
String s2("Goodbye!");
std::cout << "s1: " << s1 << std::endl;
std::cout << "s2: " << s2 << std::endl;
String s3 = s1 + s2;
std::cout << "s3: " << s3 << std::endl;
std::cout << "The last char of s3: " << s3[s3.size()-1] << std::endl;
return 0;
}
Expected result:
s1: Hello World.
s2: Goodbye!
s3: Hello World.Goodbye!
The last char of s3: !
How can I modify my code to get s3 and last char of s3 correctly?
In many of your constructors, you do not set length which leaves it with an indeterminate value - and reading such values makes the program have undefined behavior. So, first fix that:
#include <algorithm> // std::copy_n
// Constructor with no arguments
String::String() : data{new char[1]{'\0'}}, length{0} {}
// Constructor with one argument
String::String(const char* s) { // note: const char*
if (s == nullptr) {
data = new char[1]{'\0'};
length = 0;
} else {
length = std::strlen(s);
data = new char[length + 1];
std::copy_n(s, length + 1, data);
}
}
// Copy Constructor
String::String(const String& source) : data{new char[source.length + 1]},
length{source.length}
{
std::copy_n(source.data, length + 1, data);
}
// Move Constructor
String::String(String&& source) : String() {
std::swap(data, source.data);
std::swap(length, source.length);
}
In operator+= you are trying to use the subscript operator, String::operator[], but you haven't added such an operator so instead of s[i], use s.data[i]:
String& String::operator+=(const String& s) {
unsigned len = length + s.length;
char* str = new char[len + 1];
for (unsigned j = 0; j < length; j++) str[j] = data[j];
for (unsigned i = 0; i < s.length; i++) str[length + i] = s.data[i];
str[len] = '\0';
delete[] data; // note: delete[] - not delete
length = len;
data = str;
return *this;
}
If you want to be able to use the subscript operator on String objects, you would need to add a pair of member functions:
class String {
public:
char& operator[](size_t idx);
char operator[](size_t idx) const;
};
char& String::operator[](size_t idx) { return data[idx]; }
char String::operator[](size_t idx) const { return data[idx]; }
And for String s3 = s1 + s2; to work, you need a free operator+ overload:
String operator+(const String& lhs, const String& rhs) {
String rv(lhs);
rv += rhs;
return rv;
}
Also, to support printing a String like you try in your alternative main function, you need an operator<< overload. Example:
class String {
friend std::ostream& operator<<(std::ostream& os, const String& s) {
os.write(s.data, s.length);
return os;
}
};
Full demo
For starters neither constructor sets the data member length.
So the operator
String& String::operator+= (const String& s)
{
unsigned len = length + s.len();
char* str = new char[len];
//...
has undefined behavior.
Also provided that the data member length was initialized you need to write
char* str = new char[len + 1];
instead of
char* str = new char[len];
to reserve memory for the terminating zero character '\0' because you are using the standard C string function strcpy in the copy constructor
strcpy(data, source.data);
And the class does not have the subscript operator used in this for loo[
for (unsigned i=0; i < s.len(); i++)
str[length+i] = s[i];
And you forgot to append the terminating zero character '\0'.
Pay attention to that there is no member function size in the class used in this expression
s3[s3.size()-1]
And this construction
String s3 = String s1 + String s2;
is invalid. At least you should write
String s3 = s1 + s2;
and correspondingly define the operator +.

What's wrong, why is the "copy assignment operator" invoked right after "copy constructor"?

[NOTE: My first attempt to implement such class, so please, don't throw stones at me and please don't be rude. Be patient. Thanks.]
So, I have written a custom MyString Class in C++.
Running version can be found here: https://onlinegdb.com/rJMEzP9aD.
I got some strange behaviors. I am trying to get the copy constructors, copy operator and the const char* data() to work together.
EDIT #4: This is strange,
class MyString {
public:
MyString(): pSize{0}, pStr{nullptr} {}
MyString(const char* cstr):
pSize{compute_length(cstr)}, pStr{nullptr}
{
pStr = new char[pCapacity];
std::copy(cstr, cstr + pSize, pStr);
}
MyString(const MyString& rhs):
pSize{rhs.size()}, pStr{nullptr}
{
pStr = new char[pCapacity];
std::copy(rhs.data(), rhs.data() + rhs.size(), pStr);
}
~MyString()
{
if(pStr){
delete[] pStr;
}
}
size_t size() const { return pSize; }
size_t capacity() const { return pCapacity; }
const char* data() const { return pStr; }
MyString& operator=(const MyString& rhs){
if(this == &rhs)
return *this;
if(pCapacity < rhs.size()){
delete[] pStr;
pSize = rhs.pSize;
pCapacity = (pCapacity < rhs.capacity()) ? rhs.capacity() : pCapacity;
pStr = new char[pCapacity];
std::copy(rhs.data(), rhs.data() + rhs.size(), pStr);
return *this;
}
pSize = rhs.size();
pCapacity = rhs.capacity();
std::copy(rhs.data(), rhs.data() + rhs.size(), pStr);
return *this;
}
private:
size_t pSize;
char* pStr;
size_t pCapacity = 14;
inline size_t compute_length(const char* cstr)
{
if(cstr == "")
return 0;
size_t i = 0;
while(cstr[++i] != '\0');
return i;
}
}
Main.cpp,
int main()
{
MyString s("Hello");
//assert(s == "Hello");
cout << s << endl;
s = "";
cout << s << endl;
assert(s == "wow");
return 0;
}
This class supports what a regular String class supports, NOT everything, and obviously far from perfect. However, there's a const char* data() const; which returns a pointer. So, in case the previous class modifies the content of its pointer, the other pointer must also be affected. Like the STD class does. However, for my implementation this comes with some cost. Here I have no idea currently why I have the issues I have. For example: performing s = "something new";, doesn't seem to replace previous. Or, copy/move to previous allocated memory pointer e.g. when sizes of a string and local heap allocated string match vice versa.
compute_length function would return 1 when used on an empty string (try to increment iinside the loop).
Try to write your operators and constructors for char* not only for MyString
The == operators seem not to be complete.
I didn't read the code in detail, so I may have missed important stuff.
Some changes
Always store the string null terminated. It'll help interacting with other APIs.
Two friends added for std::ostream and ==.
Memory cleanup.
Capacity always set to largest allocation seen during the class lifetime.
#include <algorithm>
#include <cassert>
#include <cstring>
#include <iostream>
class MyString {
public:
MyString() : MyString(""){}
MyString(const char* cstr):
pSize{compute_length(cstr)}, pStr{new char[pSize+1]}, pCapacity{pSize}
{
std::copy_n(cstr, pSize + 1, pStr);
}
MyString(const MyString& rhs):
pSize{rhs.size()}, pStr{new char[pSize+1]}, pCapacity{pSize}
{
std::copy_n(rhs.data(), rhs.size()+1, pStr);
}
~MyString()
{
delete[] pStr;
}
size_t size() const noexcept{ return pSize; }
size_t capacity() const noexcept{ return pCapacity; }
const char* data() const noexcept{ return pStr; }
char* data() noexcept { return pStr; }
MyString& operator=(const MyString& rhs){
if(this == &rhs)
return *this;
pSize = rhs.size();
if(pCapacity < rhs.size()){
delete[] pStr;
pStr = nullptr;
pStr = new char[pSize+1];
pCapacity = pSize;
}
std::copy_n(rhs.data(), rhs.size() + 1, pStr);
return *this;
}
friend std::ostream& operator<<(std::ostream& os, const MyString& string)
{
os.write(string.pStr, string.size());
return os;
}
friend bool operator==(const MyString& lhs, const MyString& rhs)
{
return std::strcmp(lhs.pStr, rhs.pStr) == 0;
}
private:
size_t pSize{};
char* pStr{};
size_t pCapacity{};
static inline size_t compute_length(const char* cstr)
{
size_t i = 0;
while(cstr[i] != '\0')++i;
return i;
}
};
int main()
{
using std::cout;
using std::endl;
MyString s("Hello");
assert(s == "Hello");
cout << s << endl;
s = "wow";
cout << s << endl;
assert(s == "wow");
return 0;
}

Why do I keep getting a segmentation fault with my delete function

I have an assignment where I need to use an overloaded destructor to delete dynamically allocated pointers. However, when it runs, some of my pointers are deleted until a segmentation fault with one of my objects' pointers the one pointing to " and the second" made with a parameterized constructor.
I have tried to go through and make sure the delete operator had brackets (because my new operator did) I made sure the object still existed by printing out its information and address. I have tried to rewrite my allocation function, and I have tried to go over my destructor to see where it messes up.
If it helps, I have included my destructor, my allocation function, my deallocation function, and my parameterized constructor.
'''
//Destructor
MyString::~MyString()
{
buffer_deallocate();
};
void MyString::buffer_deallocate() {
cout << m_buffer << endl;
delete[](m_buffer);
m_buffer = NULL;
m_size = 0;
}
void MyString::buffer_allocate(size_t size) {
try {
m_buffer = new char[size];
m_size = size;
}
catch(bad_alloc&)
{
cout << "Errror: Unable to allocate memory" << endl;
buffer_deallocate();
}
}
//Parameterized Constructor
MyString::MyString(const char * str)
:m_size(0)
{
const char * strPtr = str;
while(*strPtr)
{
strPtr++;
m_size++;
}
buffer_allocate(m_size);
for(int i = 0; i < m_size; i++)
{
m_buffer[i] = str[i];
}
};
'''
Every time however I get the output after "and the second" to be
Segmentation fault (core dumped)
Edit: I have tried the majority of what was recommended. At least what I understood, the problem still persists, and I realize now I was kind of sparse on my code. (Please forgive me, I'm still learning.) Here is the new code along with the rest of the function file for reference:
'''
#include<iostream>
#include<string.h>
using namespace std;
#include"MyString.h"
//Default Constructor
MyString::MyString()
:m_size(0), m_buffer(NULL)
{
buffer_allocate(0);
};
//Parameterized Constructor
MyString::MyString(const char * str)
:m_size(strlen(str)+1), m_buffer(NULL)
{
buffer_allocate(m_size);
strncpy(m_buffer, str, m_size);
};
//Copy Constructor
MyString::MyString(const MyString & other)
:m_size(0), m_buffer(NULL)
{
const char * otherPtr = other.c_str();
buffer_allocate(other.size());
for(int i = 0; i < size(); i++)
{
m_buffer[i] = otherPtr[i];
}
m_buffer[m_size] = '\0';
};
//Destructor
MyString::~MyString()
{
buffer_deallocate();
};
size_t MyString::size() const
{
return m_size;
}
size_t MyString::length() const{
return m_size-1;
}
const char * MyString::c_str() const{
return m_buffer;
}
bool MyString::operator==(const MyString & other) const {
char * m_bufferPointer = m_buffer;
while(*m_bufferPointer++)
{
const char * str_ptr = other.c_str();
if(*m_buffer != *str_ptr++)
{
return 0;
}
}
return 1;
}
MyString & MyString::operator=(const MyString & rhs) {
buffer_deallocate();
buffer_allocate(rhs.size());
const char * c_strPtr = rhs.c_str();
int i;
for(i = 0; i < rhs.size(); i++)
{
this->m_buffer[i] = c_strPtr[i];
}
return *this;
}
MyString MyString::operator+ (const MyString & other_myStr) const {
char * temp_pointer;
temp_pointer;
size_t temp_size = m_size + other_myStr.size();
//New Combined Buffer for Concatanation
try {
temp_pointer = new char[temp_size];
temp_pointer = strcat(this->m_buffer, other_myStr.c_str());
}
catch(bad_alloc&)
{
cout << "Error: Unable to Allocate Memory";
return NULL;
}
return MyString(temp_pointer);
}
char & MyString:: operator[](size_t index) {
return m_buffer[index];
}
const char & MyString::operator[] (size_t index) const {
return m_buffer[index];
}
ostream & operator << (ostream& os, const MyString & myStr) {
os << myStr.m_buffer;
return os;
}
void MyString::buffer_deallocate() {
cout << "Trying to delete : " <<m_buffer << endl;
if(m_buffer){
delete[](m_buffer);
}
cout << " Success" <<endl;
m_buffer = NULL;
m_size = 0;
}
void MyString::buffer_allocate(size_t size) {
try {
m_buffer = new char[size];
m_size = size;
}
catch(bad_alloc&)
{
cout << "Errror: Unable to allocate memory" << endl;
m_size = 0;
}
}
'''
in MyString::buffer_deallocate
cout << m_buffer << endl;
requires that m_buffer be null-terminated. Unfortunately, MyString::MyString(const char * str) does not make this guarantee.
You could
for(int i = 0; i < m_size; i++)
{
cout << m_buffer[i] << endl;
}
to print the string out character by character instead, but it is probably more useful to waste a byte, null terminate, and take advantage of the Standard library
MyString::MyString(const char * str)
:m_size(0)
{
const char * strPtr = str;
while(*strPtr)
{
strPtr++;
m_size++;
}
buffer_allocate(m_size);
for(int i = 0; i < m_size; i++)
{
m_buffer[i] = str[i];
}
m_buffer[m_size] = '\0'; // add the null
}
and then
void MyString::buffer_allocate(size_t size) {
try {
m_buffer = new char[size+1]; // +1 for the null terminator
m_size = size;
}
catch(bad_alloc&) // this is a bad thing to do here. More on that later.
{
cout << "Errror: Unable to allocate memory" << endl;
buffer_deallocate();
}
}
But we can simplify this with a few library function calls.
MyString::MyString(const char * str)
:m_size(strlen(str))
{
buffer_allocate(m_size);
strcpy(m_buffer, str);
}
Addendum:
Your class may be violating the Rule of Three. If MyString does not have a copy constructor and an assignment operator to go along with the destructor any copies, deliberate or accidental, will turn a MyString into a time bomb. The destructor of one of the copies will be run before the others leaving the others without a valid m_buffer.
MyString::buffer_allocate cannot safely return void unless it allows the exception to propagate. Catching the bad_alloc leaves the object without a valid allocation at m_buffer and the rest of the program will not know this. Either every other access in the program must test for a valid buffer or engage in undefined behaviour trying to access the invalid memory. It's likely better to let the exception fall through and be caught by another part of the program that is better suited to making a decision on what to do.
MyString::buffer_allocate will leak an existing allocation if called on a MyString that already has a valid allocation at m_buffer. I recommend a
if (m_buffer)
{
delete[] m_buffer;
}
and initializing m_buffer to null in MyString's constructor
MyString(const char* str)
: m_size(std::strlen(str)), m_buffer(nullptr)
{
buffer_allocate(m_size);
std::strncpy(m_buffer, str, m_size);
}
So I had to remake this to some working code.
The problem might be that a string length must be extended with 1 to add the nul-termination. So that makes:
// prevent some MSVS warnings->errors
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
//Destructor
class MyString {
private:
size_t m_size;
char* m_buffer;
void buffer_allocate(size_t size) {
try {
m_buffer = new char[size];
m_size = size;
}
catch (std::bad_alloc&)
{
std::cout << "Errror: Unable to allocate memory\n";
// allocation failed, nothing changed, don't delete/
m_size = 0;
}
}
void buffer_deallocate() {
// printing in the buffer deallocation??
delete[] m_buffer;
m_buffer = nullptr;
m_size = 0;
}
public:
MyString(const char* str)
: m_size(std::strlen(str)+1) // add null termination
, m_buffer(nullptr)
{
buffer_allocate(m_size);
std::strncpy(m_buffer, str, m_size);
}
~MyString() {
buffer_deallocate();
}
void Print() const {
std::cout << m_buffer << '\n';
}
};
int main() {
std::string input{ "Hello World!" };
MyString myString(input.c_str());
myString.Print();
}

Implement a class want to simulate std::string but get stuck when push_back it into a vector

I have implemented a class String. When I push_back a String object to a Vector, the program gets stuck. Please help me review it if you're interested.
String.h
#pragma once
#include <iostream>
#include <memory>
#include <string>
class String {
public:
String();
String(const char *);
String(const String &);
String(const std::string &);
String(String &&) noexcept;
String &operator=(String &&) noexcept;
~String();
String &operator=(const String &);
std::string to_string() const;
friend std::ostream &operator<<(std::ostream &os, String s);
private:
std::pair<char *, char *>
alloc_n_copy(char *, char *);
void free();
static std::allocator<char> alloc;
char *beg;
char *end;
size_t length;
};
String.cpp
#include "stdafx.h"
#include <cstring>
#include "String.h"
std::allocator<char> String::alloc = std::allocator<char>();
String::String()
: beg(nullptr), end(nullptr), length(0) {}
String::String(const char *ptr) {
std::cout << "Const char constructor execute" << std::endl;
const char *tmp = ptr;
while (*tmp++ != '\0') ++length;
beg = alloc.allocate(length);
end = std::uninitialized_copy(ptr, tmp, beg);
}
String::String(const std::string &s) {
std::cout << "Const string constructor execute" << std::endl;
strcpy_s(beg, s.size(), s.c_str());
length = s.size();
char *tmp = beg;
end = tmp + length;
}
String::String(const String &s) {
std::cout << "Copy constructor execute" << std::endl;
beg = alloc.allocate(s.length);
end = std::uninitialized_copy(s.beg, s.end, beg);
}
std::pair<char *, char *>
String::alloc_n_copy(char *beg, char *end) {
auto newBeg = alloc.allocate(end - beg);
return{ newBeg, std::uninitialized_copy(beg, end, newBeg) };
}
String &String::operator=(const String &s) {
length = s.length;
auto newStr = alloc_n_copy(s.beg, s.end);
free();
beg = newStr.first;
end = newStr.second;
return *this;
}
String::String(String &&s) noexcept : beg(s.beg), end(s.end), length(s.length) {
std::cout << "Move constructor execute" << std::endl;
s.beg = s.end = nullptr;
s.length = 0;
}
String &String::operator=(String &&s) noexcept {
if (this != &s) {
beg = s.beg;
end = s.end;
length = s.length;
s.beg = s.end = nullptr;
s.length = 0;
}
return *this;
}
void String::free() {
while (length-- >= 0) {
alloc.destroy(end--);
}
alloc.deallocate(beg, length);
}
String::~String() {
free();
}
std::string String::to_string() const {
std::string s(beg);
return s;
}
std::ostream &operator<<(std::ostream &os, String s) {
std::string str(s.beg);
os << str;
return os;
}
main.cpp
int main()
{
vector<String> v;
String s1("abc");
String s2("def");
v.push_back(s1);
v.push_back(s2);
return 0;
}
result:
Const char constructor execute
Const char constructor execute
Copy constructor execute
Move constructor execute
I don't know why the second push_back is a move construction.
And when the push_back finished, the program can't exit. Is there any resource failed to release?
Thanks
The reason that your program blocks is because your destructor never terminates:
void String::free() {
while (length-- >= 0) {
alloc.destroy(--end);
}
alloc.deallocate(beg, length);
}
Since length is an unsigned type, length >= 0 is always true. You probably don't want to be decrementing length here, before it's used as argument to alloc.deallocate(). I suggest:
void String::free() {
while (end > beg) {
alloc.destroy(end--);
}
alloc.deallocate(beg, length);
}
There are other bugs, such as failing to initialise length before using it in the char const* constructor (I don't see why you don't just use std::strlen()) and failing to allocate in the std::string constructor. I recommend using a good set of warnings (I used g++ -std=c++2a -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Weffc++) and addressing them all. The problem above was identified very easily this way.
After compiling clear of warnings, then run your code under Valgrind or other memory checker to understand some of the outstanding issues.

Code Compiles and Runs on Linux, Crashes with Heap Corruption on Windows [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 3 years ago.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Improve this question
I was testing out the Microsoft 'cl' compiler using PowerShell, and I figured I'd try compiling and running an old homework assignment for a custom MyString class. The assignment in question has been previously compiled and run with g++ on Linux without any issues (and I assumed it should be correct since I got 100% for the assignment). However, when I try to compile this code on Windows with cl, or even with MinGW, it compiles without error but crashes in run-time.
I've tried compiling all my other old assignments and they run just fine on Windows (with MinGW or cl). I tried using the /Zi option for cl and ran the Visual Studio debugger, which told me that I am causing heap corruption errors. I've tried re-writing parts of the code I thought could be the problem, but I simply cannot figure it out.
I suspect the issue might be in how I'm doing my operator overloading; if that isn't the issue I'll post the rest of the code.
MyString& MyString::operator+=(const char rhs)
{
MyString temp(*this); //create temporary MyString object from 'this'
delete [] string; //clear string memory if it exists
string = NULL;
length += 1; //set new length (addition is single char)
string = new char[length]; //allocate new memory for string
strcpy(string, temp.string); //set initial return string value
string[length - 1] = rhs; //concatenate return string with added char
return *this; //return this object as reference
}
MyString& MyString::operator+=(const char* rhs)
{
MyString temp(*this); //create temporary MyString object from 'this'
delete [] string; //clear string memory if it exists
string = NULL;
length += strlen(rhs); //set new length (addition is c-string)
string = new char[length]; //allocate new memory for string
strcpy(string, temp.string); //set initial return string value
strcat(string, rhs); //concatenate return string with added c-string
return *this; //return this object as reference
}
MyString& MyString::operator+=(const MyString& rhs)
{
MyString temp(*this); //create temporary MyString object from 'this'
delete [] string; //clear string memory if it exists
string = NULL;
length += rhs.length; //set new length (addition is MyString)
string = new char[length]; //allocate new memory for string
strcpy(string, temp.string); //set initial return string value
strcat(string, rhs.string); //concatenate return string with added MyString
return *this; //return this object as reference
}
The other spot that Visual Studio kept stopping at was in part of my Professor's header file:
//casting operator: an operator to cast MyString to string type.
//This is going to be used in main() function, in order to grade your assignment.
inline operator std::string() const
{
std::stringstream os;
for(int i=0; i<length; i++)
os << string[i];
std::string str = os.str();
return str;
}
Runs fine on Linux, does not on Windows (even when successfully compiling via g++ on both).
EDIT2:
I appologize for not including all my source. I was under the impression that I wasn't supposed to, based on a bit of text during the submission process that said something to that effect.
I've followed jww's advice and redone my code to account for the null-terminator on c-strings. I'm still stuck with the same issue.
To compile for MinGW or g++ use the instructions in the comment header.
For compiling with cl use:
cl /TP /utf-8 /EHa /Fetestmystring mystring.cxx testmystring.cxx
or for VS debugging:
cl /TP /utf-8 /EHa /Zi /Fetestmystring mystring.cxx testmystring.cxx
Here is my 'mystring.cxx'
// Description: This is the function definition file for a custom
// string class.
//
// To compile, use the following:
//
// g++ -ansi -pedantic -Wall mystring.cxx testmystring.cxx -o testmystring
//
// To run, use the following:
//
// ./testmystring
//
// Acknowledgements:
// 1. I used Dr. ####'s startup files.
// 2. I referred to https://en.cppreference.com/w/ to find some of the functions
// that I ended up using.
//=====================================================================
#include <iostream>
#include <assert.h>
#include <string.h>
#include "mystring.h"
MyString::MyString()
{
length = 0; //default constructor, initiate to '0' and 'NULL'
string = NULL;
}
MyString::MyString(const char src)
{
length = 1; //set length (char)
string = new char[length + 1]; //allocate memory for string
string[0] = src; //set string
string[1] = '\0';
}
MyString::MyString(const char* src)
{
length = strlen(src); //set length (c-string)
string = new char[length + 1]; //allocate memory for string
strcpy(string, src); //set string via copy
string[length] = '\0';
}
MyString::MyString(const MyString& src)
{
length = src.length; //copy constructor; copy length
string = new char[length + 1]; //allocate memory of correct length
strcpy(string, src.string); //deep copy the string
string[length] = '\0';
}
MyString::~MyString()
{
delete [] string; //destructor, safely delete allocated memory
string = NULL;
}
int MyString::GetLength() const
{
return length; //simple getter, nothing complicated
}
char MyString::GetCharAt(int index) const
{
return string[index]; //simple getter, could add out-of-bounds error checking
}
void MyString::SetCharAt(int index, char ch) const
{
string[index] = ch; //simple setter, could add out-of-bounds error checking
}
MyString& MyString::operator= (const char rhs)
{
delete [] string; //clear string memory if it exists
string = NULL;
length = 1; //set length (input is single char)
string = new char[length + 1]; //allocate memory for string
string[0] = rhs; //initiate string
string[1] = '\0';
return *this; //return this object as reference
}
MyString& MyString::operator= (const char* rhs)
{
delete [] string; //clear string memory if it exists
string = NULL;
length = strlen(rhs); //set length (input is c-string)
string = new char[length + 1]; //allocate memory for string
strcpy(string, rhs); //initiate string
string[length] = '\0';
return *this; //return this object as reference
}
MyString& MyString::operator= (const MyString& rhs)
{
delete [] string; //clear string memory if it exists
string = NULL;
length = rhs.length; //copy length
string = new char[length + 1]; //allocate new string of correct length
strcpy(string, rhs.string); //deep copy implementation
string[length] = '\0';
return *this; //return this object as reference
}
MyString MyString::operator+ (const char rhs) const
{
MyString ret; //create string object for returning
ret.length = length + 1; //initiate return string length (current + 1)
ret.string = new char[ret.length + 1]; //allocate return string memory
strcpy(ret.string, string); //set initial return string value
ret.string[ret.length - 1] = rhs; //concatenate return string with added char
ret.string[ret.length] = '\0';
return ret; //return addition result
}
MyString MyString::operator+ (const char* rhs) const
{
MyString ret; //create string object for returning
ret.length = length + strlen(rhs); //initiate return string length (current + new)
ret.string = new char[ret.length + 1]; //allocate return string memory
strcpy(ret.string, string); //set initial return string value
strcat(ret.string, rhs); //concatenate return string with added c-string
ret.string[ret.length] = '\0';
return ret; //return addition result
}
MyString MyString::operator+ (const MyString& rhs) const
{
MyString ret; //create string object for returning
ret.length = length + rhs.length; //initiate return string length (current + new)
ret.string = new char[ret.length + 1]; //allocate return string memory
strcpy(ret.string, string); //set initial return string value
strcat(ret.string, rhs.string); //concatenate return string with added MyString
ret.string[ret.length] = '\0';
return ret; //return addition result
}
MyString& MyString::operator+=(const char rhs)
{
MyString temp(*this); //create temporary MyString object from 'this'
delete [] string; //clear string memory if it exists
string = NULL;
length += 1; //set new length (addition is single char)
string = new char[length + 1]; //allocate new memory for string
strcpy(string, temp.string); //set initial return string value
string[length - 1] = rhs; //concatenate return string with added char
string[length] = '\0';
return *this; //return this object as reference
}
MyString& MyString::operator+=(const char* rhs)
{
MyString temp(*this); //create temporary MyString object from 'this'
delete [] string; //clear string memory if it exists
string = NULL;
length += strlen(rhs); //set new length (addition is c-string)
string = new char[length + 1]; //allocate new memory for string
strcpy(string, temp.string); //set initial return string value
strcat(string, rhs); //concatenate return string with added c-string
string[length] = '\0';
return *this; //return this object as reference
}
MyString& MyString::operator+=(const MyString& rhs)
{
MyString temp(*this); //create temporary MyString object from 'this'
delete [] string; //clear string memory if it exists
string = NULL;
length += rhs.length; //set new length (addition is MyString)
string = new char[length + 1]; //allocate new memory for string
strcpy(string, temp.string); //set initial return string value
strcat(string, rhs.string); //concatenate return string with added MyString
string[length] = '\0';
return *this; //return this object as reference
}
bool MyString::operator==(const MyString& rhs) const
{
if(rhs.length != length)
return false; //if string length's are != then return false and break
for(int i = 0; i < length; i++) //loop through string
if(rhs.string[i] != string[i]) //compare each char
return false; //if chars != then return false and break
return true; //if you got to here, strings are ==, so return true
}
bool MyString::operator==(const char* rhs) const
{
if(static_cast<int>(strlen(rhs)) != length) //static cast to get rid of signed/unsigned warning
return false; //if string length's are != then return false and break
for(int i = 0; i < length; i++) //loop through string
if(rhs[i] != string[i]) //compare each char
return false; //if chars != then return false and break
return true; //if you got to here, strings are ==, so return true
}
bool MyString::operator!=(const MyString& rhs) const
{
if(rhs.length != length)
return true; //if string length's are != then return true and break
for(int i = 0; i < length; i++) //loop through string
if(rhs.string[i] != string[i]) //compare each char
return true; //if chars != then return true and break
return false; //if you got to here, strings are ==, so return false
}
bool MyString::operator!=(const char* rhs) const
{
if(static_cast<int>(strlen(rhs)) != length) //static cast to get rid of signed/unsigned warning
return true; //if string length's are != then return true and break
for(int i = 0; i < length; i++) //loop through string
if(rhs[i] != string[i]) //compare each char
return true; //if chars != then return true and break
return false; //if you got to here, strings are ==, so return false
}
std::ostream& operator<< (std::ostream& os, const MyString& str)
{
return os << str.string; //pass string through ostream and return as reference (for loop as alternative?)
}
std::istream& operator>> (std::istream& is, MyString& str)
{
char temp[4096]; //temporary, arbitrarily sized array (would prefer a better method)
delete [] str.string; //clear string memory if it exists
str.string = NULL;
is.read(temp, sizeof temp); //read input into local temp array
str.length = is.gcount(); //set string length to size of input
str.string = new char[str.length + 1]; //create new string of correct length
strcpy(str.string, temp); //move input from temp to string
str.string[str.length] = '\0';
return is; //return istream as reference
}
My Professor's provided 'mystring.h'
* Description: *
* *
* This is a header file for MyString class. *
* Do not modify this file. Implement methods in MyString.cxx. *
* *
* Please report any bug to ####(professor's email, hidden for privacy) *
*************************************************************************/
#ifndef __mystring_h__
#define __mystring_h__
#include <iostream>
#include <string>
#include <sstream>
#include <assert.h>
using namespace std;
class MyString
{
private:
int length; // length of a string
char* string; // string pointer. Its size must be length.
public:
// casting operator: an operator to cast MyString to string type.
// This is going to be used in main() function, in order to grade your assignment.
inline operator std::string() const
{
std::stringstream os;
for(int i=0; i<length; i++)
os << string[i];
std::string str = os.str();
return str;
}
// index operator []: an operator that allows array like operation. ( ex: a[3] )
// This is going to be used for test 08 in main() function, in order to grade your assignment.
char operator[] (int index)
{
if(index < 0 ) throw "out of index in operator[]";
if(index >= length) throw "out of index in operator[]";
return string[index];
}
public:
MyString();
MyString(const char src); // create a string from a char
MyString(const char* src); // create a string from C-string
MyString(const MyString& src); // copy constructor
~MyString(); // destructor
int GetLength() const; // getter
char GetCharAt(int index) const; // getter
void SetCharAt(int index, char ch) const; // setter
// assignment operator
MyString& operator= (const char rhs); // operator =
MyString& operator= (const char* rhs); // operator =
MyString& operator= (const MyString& rhs); // operator =
MyString operator+ (const char rhs) const; // operator +
MyString operator+ (const char* rhs) const; // operator +
MyString operator+ (const MyString& rhs) const;// operator +
MyString& operator+=(const char rhs); // operator +=
MyString& operator+=(const char* rhs); // operator +=
MyString& operator+=(const MyString& rhs); // operator +=
// equality/inequality operators
bool operator==(const MyString& rhs) const; // operator ==
bool operator==(const char* rhs) const; // operator ==
bool operator!=(const MyString& rhs) const; // operator !=
bool operator!=(const char* rhs) const; // operator !=
// stream insertion/extraction operators as friend
friend std::ostream& operator<< (std::ostream& os, const MyString& str);
friend std::istream& operator>> (std::istream& is, MyString& str);
};
#endif
My Professor's provided 'testmystring.cxx'
* Description: *
* *
* This is a test program for class MyString. *
* Do not modify this file. *
* *
* Please report any bug to ##### (Proff's email) *
*************************************************************************/
#include <iostream>
#include <fstream>
#include <sstream>
#include <limits>
#include <cmath>
#include <string>
#include <string.h>
#include "mystring.h"
using namespace std;
template<typename T>
void TestOutput(int& num_tests, int& num_success, T value, T expect, string testname)
{
num_tests++;
cout << endl << testname << endl;
cout << " result: " << value << endl;
cout << " expect: " << expect << endl;
if (value == expect)
{
num_success++;
cout << " test success" << endl;
}
else
{
cout << " test failed !!!!!" << endl;
}
}
int main()
{
int num_tests = 0; // total number of tests
int num_success = 0; // number of tests succeeded
try
{
// test constructors
MyString t01; TestOutput<string>(num_tests, num_success, (string)t01, "" , "test 01 - default constructor");
MyString t02('c'); TestOutput<string>(num_tests, num_success, (string)t02, "c" , "test 02 - constructor: MyString(const char)");
MyString t03("abc"); TestOutput<string>(num_tests, num_success, (string)t03, "abc" , "test 03 - constructor: MyString(const char*)");
MyString t04(t03); TestOutput<string>(num_tests, num_success, (string)t04, "abc" , "test 04 - copy constructor");
// test getters and setters
TestOutput<int >(num_tests, num_success, t04.GetLength() , 3 , "test 05 - GetLength()");
TestOutput<char >(num_tests, num_success, t04.GetCharAt(0) , 'a' , "test 06 - GetCharAt()");
TestOutput<char >(num_tests, num_success, t04.GetCharAt(1) , 'b' , "test 07 - GetCharAt()");
TestOutput<char >(num_tests, num_success, t04[2] , 'c' , "test 08 - operator[] (int)");
t04.SetCharAt(2, 'x'); TestOutput<string>(num_tests, num_success, (string)t04 , "abx" , "test 09 - SetCharAt()");
// operators
MyString t05, t06;
t05 = 's'; TestOutput<string>(num_tests, num_success, (string)t05, "s" , "test 10 - operator=(const char)");
t05 = "qwert"; TestOutput<string>(num_tests, num_success, (string)t05, "qwert" , "test 11 - operator=(const char*)");
t06 = t05 = " zxc"; TestOutput<string>(num_tests, num_success, (string)t05, " zxc" , "test 12 - operator=(const char*)");
TestOutput<string>(num_tests, num_success, (string)t06, " zxc" , "test 13 - operator=(const MyString&)");
MyString t07;
t07 = t03 + '_'; TestOutput<string>(num_tests, num_success, (string)t07, "abc_" , "test 14 - operator+(const char) const");
t07 = t03 + "_1234"; TestOutput<string>(num_tests, num_success, (string)t07, "abc_1234" , "test 15 - operator+(const char*) const");
t07 = t03 + t05; TestOutput<string>(num_tests, num_success, (string)t07, "abc zxc" , "test 16 - operator+(const MyString&) const");
t07 += '#'; TestOutput<string>(num_tests, num_success, (string)t07, "abc zxc#" , "test 17 - operator+=(const char)");
t07 += "hjkl"; TestOutput<string>(num_tests, num_success, (string)t07, "abc zxc#hjkl" , "test 18 - operator+=(const char*)");
t07 += t07; TestOutput<string>(num_tests, num_success, (string)t07, "abc zxc#hjklabc zxc#hjkl" , "test 19 - operator+=(const MyString&)");
MyString t08="siejfin";
MyString t09="siejfin";
MyString t10="siejfi_";
MyString t11="siejfin ";
TestOutput<bool >(num_tests, num_success, t08 == t09 , true , "test 20 - operator==(const MyString&) const");
TestOutput<bool >(num_tests, num_success, t08 == t10 , false, "test 21 - operator==(const MyString&) const");
TestOutput<bool >(num_tests, num_success, t08 == t11 , false, "test 22 - operator==(const MyString&) const");
TestOutput<bool >(num_tests, num_success, t03 == "abc", true , "test 23 - operator==(const char*) const");
TestOutput<bool >(num_tests, num_success, t03 == "ab" , false, "test 24 - operator==(const char*) const");
TestOutput<bool >(num_tests, num_success, t08 != t09 , false, "test 25 - operator!=(const MyString&) const");
TestOutput<bool >(num_tests, num_success, t08 != t10 , true , "test 26 - operator!=(const MyString&) const");
TestOutput<bool >(num_tests, num_success, t08 != t11 , true , "test 27 - operator!=(const MyString&) const");
TestOutput<bool >(num_tests, num_success, t03 != "abc", false, "test 28 - operator!=(const char*) const");
MyString t12("test-stream");
{
// save data of t12 to a file
ofstream infile;
infile.open("./testMyString.data");
infile << t12;
infile.close();
// read MyString from the file
ifstream outfile;
outfile.open("./testMyString.data");
MyString tmp;
outfile >> tmp;
outfile.close();
// compare read file with solution
TestOutput<string>(num_tests, num_success, (string)tmp, "test-stream" , "test 29 - operator <<, operator >>");
}
MyString t13("HelloWorld!");
{
// save data of t12 to a file
ofstream infile;
infile.open("./testMyString.data");
infile << t13;
infile.close();
// read MyString from the file
ifstream outfile;
outfile.open("./testMyString.data");
MyString tmp;
outfile >> tmp;
outfile.close();
// compare read file with solution
TestOutput<string>(num_tests, num_success, (string)tmp, "HelloWorld!", "test 30 - operator <<, operator >>");
}
}
catch(const char* message)
{
cout << "Exception: " << message << endl << endl;
}
catch(...)
{
cout << "Exception" << endl;
}
// print test results
int total_tests = 30;
if(num_success == total_tests)
{
cout << endl;
cout << "Congratulation!" << endl;
cout << "Your program passed all " << num_success << " test cases." << endl;
}
else
{
cout << endl;
cout << "Your program failed " << (total_tests - num_success) << " cases out of " << total_tests << " cases." << endl;
}
cout << endl;
return 0;
}
MyString::MyString(const char* src)
{
length = strlen(src); //set length (c-string)
string = new char[length]; //allocate memory for string
strcpy(string, src); //set string via copy
}
You are missing space for the terminating NULL on the constructor.
That is the start of the spiral when the copy ctor is invoked:
MyString& MyString::operator+=(const char* rhs)
{
MyString temp(*this); //create temporary MyString object from 'this'
...
}
So maybe something like this in the copy ctor:
string = new char[length+1];
But in operator+= you need to make room for the old string and new string. So you really want to allocate:
string = new char[length+strlen(rhs)+1];
Then use strcpy and strcat to concatenate the two strings. Maybe something like:
MyString& MyString::operator+=(const char* rhs)
{
size_t new_length = length+strlen(rhs)+1;
char* new_string = new char[new_length];
strcpy(new_string, string);
strcat(new_string, rhs);
std::swap(new_string, string);
std::swap(new_length, length);
delete[] new_string;
return *this;
}
You may have more issues, but that one jumped out.
The assignment in question has been previously compiled and run with g++ on Linux without any issues (and I assumed it should be correct since I got 100% for the assignment).
I believe Linux has issues too. You just did not crash.
Try using Valgrind or Address Sanitizer on Linux. They should produce findings for the code.