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
Related
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 +.
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;
Can anybody explain me, why in my below code, where ms3 = ms1 is done, both copy and assignment operator get called in this line. In the above mentioned line only overloaded assignment operator should get called as per my knowledge. But both the copy constructor and assignment operator are getting called. Please explain me.. why this happens?
class MyString {
private:
char* string;
public:
MyString(char *ptr = NULL);
MyString(MyString &str);
MyString & operator =(MyString str);
~MyString();
};
MyString::MyString(MyString &str) {
printf("Copy Constructor called !!!\n");
int len = strlen(str.string);
string = new char[len+1];
strcpy_s(string, len+1, str.string);
}
MyString::MyString(char* str) {
printf("Constructor called !!!\n");
if (str != NULL) {
int len = strlen(str);
string = new char[len + 1];
strcpy_s(string, len + 1, str);
}
else {
string = NULL;
}
}
MyString & MyString::operator=(MyString str) {
printf("Assignment Operator!!!\n");
if (&str == this) {
return *this;
}
delete[] string;
if (&str != NULL) {
int len = strlen(str.string);
string = new char[len + 1];
strcpy_s(string, len+1, str.string);
}
return *this;
}
MyString::~MyString() {
printf("Destructor\n");
delete[] string;
}
int _tmain(int argc, _TCHAR* argv[])
{
MyString ms = "ABC";
MyString ms1("EFG");
MyString ms2 = ms1;
MyString ms3;
ms3 = ms1;
MyString ms4 = ms3 = ms1;
return 0;
}
The assignment operator takes its argument by value; the copy constructor is used to set up that argument.
To avoid this, your need to rewrite your assignment operator so that it takes a reference, like the copy constructor does. Also you should use const :
MyString(MyString const &str);
MyString & operator=(MyString const &str);
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);
}
I'm having trouble with dynamic allocation.In my code
am I initializing the dynamic array correctly?.
When I try to write + operator member for my class String, it doesn't return what I want. Any guidance to the correct path would be great.
Ex.
String s1("One");
String s2("Two");
String s3 = s1+ s1;
cout <<s3;
//Output is OneTwo
cout <<s1;
//OUtput is OneTwo.
Also I don't understand why I can not add delete[] buf into my constructor.
class String{
public:
String (const char *s =""):buf(new char[strlen(s)]){
buf = strdup(s);
};
String (const String &s):buf(new char[strlen(s.buf)]){
buf = strdup(s.buf);
delete []buf;
};
String operator =(const String &s){
return buf =strdup(s.buf);
};
char & operator [] (int index){
assert(inBounds(index));
return buf[index];
};
int size()
{
return strlen(buf);
};
String operator + (const String s){
delete []buf;
char *temp = new char[strlen(buf)+strlen(s.buf)];
///NEed to ask about t*his acan get this operator tor work
cout<< s.buf;
return temp;
};
String operator += (const String s){
strcpy(buf + strlen(buf),s.buf);
return buf;
};
void print(ostream & out){
out << buf;
};
void read (istream & in){
in >> buf;
};
~String(){
//delete [] buf;
};
private:
bool inBounds(int x){
return x >= 0 && x < strlen(buf);
};
static int strlen(const char *s){
int len =0;
for(int i=0;s[i] != '\0';i++)
len++;
return len;
};
static char *strcpy(char *dest,const char *src){
int i=0;
for(;(dest[i] = src[i]); ++i);
dest[i] = '\0';
return dest;
};
static char *strdup(const char *s){
char * buf;
buf = new char[strlen(s)+1];
int i=0;
for(;s[i] != '\0';i++)
buf[i] = s[i];
buf[i] = '\0';
return buf;
}
char * buf;
};
Your first constructor
String (const char *s ="") : buf(new char[strlen(s)]){
buf = strdup(s);
}
first allocates a buffer that's one character too small, then it throws it away by pointing bufto the result of strdup – a memory leak.
You want
String (const char *s ="") : buf(new char[strlen(s) + 1]){
strcpy(buf, s);
}
or
String (const char *s ="") : buf(strdup(s))
{
}
Your second constructor
String (const String &s) : buf(new char[strlen(s.buf)]){
buf = strdup(s.buf);
delete []buf;
};
has the same problem with a memory leak, and has the added complication that you immediately deallocate buf.
You want something like
String (const String& s) : buf(strdup(s.buf))
{
}
Your + deallocates buf, allocates an uninitialised (and too small) buffer, prints buf(which is undefined) and then returns a String made from the uninitialised buffer.
The addition operator should not modify *this; it should use += and look like
String operator+ (const String& s) const
{
String result = *this;
result += s;
return result;
};
Which leaves +=, which needs to reallocate buf to be large enough to hold the result.
I'll leave it as an exercise.
And reimplementing standard library functions using the standard name is very confusing.