passing parameters to the concatenate function - c++

i'm confused with *this = *this +rhs; , should i use this = this +rhs; instead or it will be wrong because the Mystring Mystring::operator+(Mystring &rhs) const expect object not reference of object???
i'm using the concatenate function to do the assign and concatenate.
////concatenate
Mystring Mystring::operator+(Mystring &rhs) const
{
std::cout << "concatenate" << std::endl;
size_t buff_size = strlen(this.str)+strlen(rhs.str)+1;
char *buff = new char[buff_size];
std::strcpy(buff,str);
std::strcat(buff,rhs);
Mystring temp {buff};
delete [] buff;
return temp;
}
//concatenate and assign s1= s1+ ****
Mystring &Mystring::operator+=(const Mystring &rhs)
{
*this = *this +rhs;
return *this;
}
//repeat s1 = s2 * 3
Mystring Mystring::operator*(int n) const
{
Mystring temp;
for(size_t i=0;i<n;++i)
temp = temp + *this;
return temp;
}

Look at it like this:
this = this + rhs;
// is the same as:
pointer_to_MyString = pointer_to_MyString + MyString;
That doesn't make any sense!
You need to deference them first. Or in other words, remove the "pointer"
*this = *this + rhs;
// is the same as:
MyString = MyString + MyString;

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 +.

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.

Memory deallocation of temporary object c++

I build a simple string class.
I try to make the concatenation functions which one of them is + and the other +=.
When trying to implement += I generate a Str object under that equals the 1st string and which size is s.size(). But then when I tried to append to it a new string t I need to free the old array s string and allocate new size for it. After the destructor of the temp Str object is called it stucks there when freeing the old space and I can't understand why. How can I deallocate the Str under the + member function?
class Str
{
public:
typedef size_t size_type;
typedef char* iterator;
typedef const char* const_iterator;
iterator begin(){ return p; }
iterator end() { return p + std::strlen(p); }
const_iterator begin() const { return p; }
const_iterator end() const { return p + std::strlen(p); }
size_type size() const { return data_length; }
Str() {};
Str(const Str& s):
p(new char[s.size() +1]),
data_length(s.size())
{
std::copy(s.begin(), s.end(), p);
p[data_length] = '\0';
}
Str(const char* cp) :
p(new char[std::strlen(cp) + 1 ]),
data_length(std::strlen(cp))
{
std::copy(cp, cp+ std::strlen(cp) + 1,p);//copies also the '\0' char to the last place in p
}
Str& operator=(Str& rhs)//assignment operator
{
if (&rhs != this)
{
uncreate();
create(rhs.size());
std::copy(rhs.begin(), rhs.end() + 1, p);
//p[rhs.size()] = '\0';
}
return *this;
}
Str& operator=(const char* cp)//assignment operator
{
if (cp!= p)
{
uncreate();
create(std::strlen(cp));
std::copy(cp, cp+std::strlen(cp), p);
p[data_length] = '\0';
}
return *this;
}
Str& operator+=(const Str&);
~Str()
{
delete[] p;//stucked here while returning from + member function
data_length = 0;
}
const char* c_str() const;
void copy(char* ,size_type);
private:
char* p;
size_type data_length = 0;
const_iterator ci() const { return p; }
void uncreate();
void create(size_type);
};
Str operator+(const Str& s, const Str& t)
{
Str r = s;
r += t;
return r;
}
inline Str& Str::operator+=(const Str &s)
{
//trying to allocate new space for this object
std::copy(s.begin(),s.end(),p+this->size());
p[data_length] = '\0';
return *this;
}
void Str::create(Str::size_type n)
{
p = new char[n + 1];
data_length = n;
}
void Str::uncreate()
{
delete[] p;//to check that p is allocated right
data_length = 0;
}
The main for example:
int main()
{
Str s1 = "hello";
Str s2 = "worly";
Str s3 = s1 + s2;
return 0;
}
I suppose you want something like this:
inline Str& Str::operator+=(const Str &s)
{
const int new_data_length = data_length + s.data_length;
char * temp = new char[new_data_length + 1];
memcpy(temp, p, data_length);
memcpy(temp + data_length, s.p, s.data_length);
delete [] p;
p = temp;
data_length = new_data_length;
p[data_length] = 0;
return *this;
}

Is there a memory leak?

I have tried to write my own String class in C++ using Microsoft Visual Studio 2015. I wrote the class like this;
#include<string.h>
class myString {
private:
char* content;
public:
int size;
myString();
myString(char*);
~myString();
bool operator== (const myString &) const;
bool operator!= (const myString &) const;
myString operator= (const myString &);
myString operator+ (const myString &) const;
myString operator+= (const myString &);
friend std::ostream& operator<< (std::ostream &os, const myString &);
char operator[] (int &) const;
};
std::ostream& operator<<(std::ostream &os, const myString &string) {
os << string.content;
return os;
}
myString::myString() {
size = 0;
content = "\0";
}
myString::myString(char* newContent) {
size = strlen(newContent);
content = new char[size+1];
strcpy(content, newContent);
}
myString::~myString() {
delete[] content;
}
myString myString::operator= (const myString &string) {
if (size != string.size) {
delete[] content;
size = string.size;
content = new char[size+1];
}
strcpy(content, string.content);
return *this;
}
bool myString::operator== (const myString &string) const {
if (size != string.size)
return false;
if (strcmp(content, string.content))
return false;
return true;
}
bool myString::operator!= (const myString &string) const {
if (*this == string)
return false;
return true;
}
myString myString::operator+ (const myString &string) const {
int newSize = size + string.size;
char* newContent = new char[newSize];
strcpy(newContent, content);
strcat(newContent, string.content);
return myString(newContent);
}
myString myString::operator+= (const myString &string) {
*this = *this + string;
return *this;
}
char myString::operator[] (int &index) const {
return content[index];
}
It works fine when I tried to do this;
#include<iostream>
#include "MyString.h"
using namespace std;
int main() {
myString s("my new");
cout << s+" string" << endl;
}
But I am not sure if there is any memory leak in operator+ function in the line char* newContent = new char[newSize]; I am allocating new space from the memory and I need it in the return statement return myString(newContent);.
So I can not deallocate it before this line and I can not deallocate it after the return statement. Am I correct, is there a memory leak? If so, how can I fix this?
EDIT 1 :
I've changed the operator+ function as follows with the help of Prince Dhaliwal;
myString myString::operator+ (const myString &string) const {
myString temp;
int newSize = size + string.size;
char* newContent = new char[newSize + 1];
temp.size = newSize;
strcpy(newContent, content);
strcat(newContent, string.content);
temp.content = newContent;
return temp;
}
But since I created the temp locally it calls its destructor before returning it and gives error. I supposed I should allocate memory for temp too. And I changed the function as follows;
myString myString::operator+ (const myString &string) const {
myString* temp= new myString;
int newSize = size + string.size;
char* newContent = new char[newSize+1];
temp->size = newSize;
strcpy(newContent, content);
strcat(newContent, string.content);
temp->content = newContent;
return *temp;
}
It works fine now, but I believe there is still memory leak because of the temp variable. If there is a memory leak, how to fix this?
EDIT 2 :
I've fixed it simply by creating a Copy Constructor
There is actually a memory leak in your code. When you are using the + operator in s + " string". In your operator+() definition i.e.
myString myString::operator+ (const myString &string) const {
int newSize = size + string.size;
char* newContent = new char[newSize];
strcpy(newContent, content);
strcat(newContent, string.content);
return myString(newContent);
}
You are allocating the new string here char* newContent = new char[newSize];, copying the older and new part to the new string. And again you are allocating the new string in the constructor return myString(newContent);. But where are you deleting your old string? Its nowhere in your code. So you have to delete the string newContent.
You can do this
myString myString::operator+ (const myString &string) const {
myString temp;
int newSize = size + string.size;
char* newContent = new char[newSize + 1];
temp.size = newSize;
strcpy(newContent, content);
strcat(newContent, string.content);
temp.content = newContent;
return temp;
}
UPDATE
You have to create a copy constructor.
myString(const myString &rhs) :
size(rhs.size) {
content = new char[size + 1];
strcpy(content, rhs.content);
}

Concatenate two customized string objects in C++?

I am working on defining my own string class for a homework. It comes to my attention that the following code
class MyString {
public:
MyString(const char* s = NULL) {len = strlen(s); str = new char[len + 1]; strcpy(str, s);}
~MyString() {delete [] str;}
friend ostream& operator << (ostream& ost, const MyString& s) { ost << s.str; return ost;}
friend MyString operator + (const MyString &s1, const MyString &s2) {
int length = strlen(s1.str) + strlen(s2.str);
char *str = new char[length + 1];
strcpy(str, s1.str);
strcat(str, s2.str);
return MyString(str);
}
private:
char * str;
int len;
};
int main () {
MyString s1 = MyString("hello");
MyString s2 = MyString("world");
cout << s1 + s2 << endl;
return 0;
}
works, as the return object is created at the last moment. But the following code
class MyString {
public:
MyString(const char* s = NULL) {len = strlen(s); str = new char[len + 1]; strcpy(str, s);}
~MyString() {delete [] str;}
friend ostream& operator << (ostream& ost, const MyString& s) { ost << s.str; return ost;}
friend MyString operator + (const MyString &s1, const MyString &s2) {
int length = strlen(s1.str) + strlen(s2.str);
MyString temp;
temp.str = new char[length + 1];
strcpy(temp.str, s1.str);
strcat(temp.str, s2.str);
return temp;
}
private:
char * str;
int len;
};
int main () {
MyString s1 = MyString("hello");
MyString s2 = MyString("world");
cout << s1 + s2 << endl;
return 0;
}
Does not, giving me a run time error. So I am confused by why the second approach fails, if a temporary object is defined, modified and returned in the overloaded operator.
The issue is that when you default-construct temp here:
MyString temp;
You have to execute:
MyString(const char* s = NULL) {len = strlen(s); ... }
strlen on a null pointer is undefined. It would work if you instead changed the default argument to:
MyString(const char* s = "")
However, both solutions are still poor in that they both leak memory. In the former, you never delete[] the temporary str. In the latter, your default constructor allocated a new str member, and then you immediately override it with a new allocated str member. The original is leaked.
in your constructor you allocate your memory for your string:
len = strlen(s); str = new char[len + 1]; strcpy(str, s);
so if you append a string later on; there is no memory allocated therefore.
When creating the sum of your two string; you create your "sum-string" like this:
MyString temp;
then the memory allocated for str is unknown; since
len = strlen(NULL);
If you want to continue along; you might consider adding two things:
a check whether s = NULL; then not allocate memory but this will cause trouble later on...
a method to allocate more memory; something like temp.allocate(strlen(s1) + strlen(s2))
The return is creating an object and copying pointer. Then 2 deletes occur on same address