This is the method used to handle addition of MyString class objects.
Everything runs fine until the delete [] buff - statement.
using visual studio community
MyString MyString::operator + (const MyString &rhs) const {
size_t buff_size{ std::strlen(this->str) + 1 };
char *buff = new char[buff_size];
strcpy_s(buff, buff_size, str);
size_t noOfEls{ std::strlen(str) + std::strlen(rhs.str) + 1 }; // total length + null terminator
strcat_s(buff, noOfEls, rhs.str);
MyString temp{ buff };
delete[] buff; // complier error here
return temp;
}
So I finally figured out the issue in my code after I tried to write a non member function which is a friend of the MyString class and the function worked with out any errors.
I wondered what I did right and found out that the buffer memory allocated wasn't actually enough to store data incoming from the right hand side paramter (rhs variable).
Here is the fixed code and delete [] buff worked fine as expected.
MyString MyString::operator + (const MyString &rhs) const {
size_t buff_size{ std::strlen(this->str) + std::strlen(rhs.str) + 1 };
char *buff = new char[buff_size];
strcpy_s(buff, buff_size, str);
strcat_s(buff, buff_size, rhs.str);
MyString temp{ buff };
delete[] buff;
return temp;
}
If you take a closer look at the buff_size variable, you'll see that I allocated memory for both the length of the current object(this) and the incoming rhs object(NB: plus the null terminator) as opposed to the initial code I wrote above
Related
For some reason, whenever it reassigns the string as a different const char*, it has an error. After it creates a new buffer on a string with a previous buffer, when it exists the constructor the buffer changes value for no reason. I am using heap memory, so it shouldn't be affected by scope.
#include <iostream>
class String {
private:
char* buffer;
unsigned int size;
public:
String(const char* string) {
size = strlen(string);
buffer = new char[size + 1];
memcpy(buffer, string, size + 1);
buffer[size] = 0;
}
String(const String& other) {
memcpy(this, new String(other.buffer), sizeof(String));
}
~String() {
delete buffer;
}
friend std::ostream& operator<<(std::ostream& stream, const String& str);
String operator+(const String& other) {
unsigned int nSize = this->size + other.size;
char* nBuffer = new char[nSize + 1];
memcpy(nBuffer, this->buffer, nSize + 1);
strcat(nBuffer, other.buffer);
nBuffer[nSize] = 0;
return String(nBuffer);
}
void operator+=(const String& other) {
unsigned int nSize = this->size + other.size;
char* nBuffer = new char[nSize + 1];
memcpy(nBuffer, this->buffer, nSize + 1);
strcat(nBuffer, other.buffer);
nBuffer[nSize] = 0;
this->~String();
memcpy(this, new String(nBuffer), sizeof(String));
}
};
std::ostream& operator<<(std::ostream& stream, const String& str) {
stream << str.buffer;
return stream;
}
int main() {
String test1 = "Hello";
test1 = "there";
std::cout << test1 << std::endl;
}
test1 = "there" is the same as test1 = String("there") because the string "there" has to be converted to a String. The default assignment operator works by assigning all the members on the right hand side to the members on the left hand side. It does a shallow copy of all the members, so your strings are not actually duplicated, but rather both buffer pointers in test1 and String("there") point to the same memory location that holds the string "there".
At the end of the assignment the String("there") object has to be destroyed, so it calls the destructor which deletes the memory that its buffer is pointing to. At the end of the main() method the same happens with test1, only when it gets there it finds that the memory has already been deleted, causing a runtime error.
When dealing with raw pointers as class members, in addition to making your own copy-constructor you also have to make your own copy-assignment operator to delete the already held memory and allocate new memory. A simply one may look like this:
String& operator=(String const& other) {
delete[] buffer;
buffer = new char[other.size + 1];
strcpy(buffer, other.buffer);
size = other.size;
return *this;
}
Now assigning into test1 will allocate an entirely seperate memory space to hold the string.
I'm trying to write a simple version of the string class (for practice), and I have everything working except the overloaded + operator.
The line " strcpy_s(temp, strlen(stringPtr) + 1, stringPtr); " keeps throwing an exception. I assume strcat_s will too.
Any advice?
MyString MyString::operator+(const MyString & other)
{
if (this != &other)
{
char * temp = new char[strlen(stringPtr) + strlen(other.stringPtr) + 1];
strcpy_s(temp, strlen(stringPtr) + 1, stringPtr);
strcat_s(temp, strlen(other.stringPtr) + 1, other.stringPtr);
delete[]stringPtr;
stringPtr = temp;
delete[]temp;
}
return this->stringPtr;
}
If it helps, stringPtr is being passed "bob," and other.stringPtr is being passed "sally."
You should pass the same size to both functions.
MyString MyString::operator+(const MyString & other)
{
size_t newSize = strlen(stringPtr) + strlen(other.stringPtr) + 1;
char * temp = new char[newSize];
temp[0] = 0;
strcpy_s(temp, newSize, stringPtr);
strcat_s(temp, newSize, other.stringPtr);
//I'm assuming your constructor makes a copy.....
MyString ret(temp);
delete[] temp;
return ret;
}
You might look at this for more info on a better way to implement some operators. E.g., operator+ is often implemented in terms of operator+=.
I'm currently implementing my own string class (just for training), but I'm experiencing some problems in my substr() method:
MyString MyString::substr(size_t position, size_t length)
{
if (checkBounds() || length == 0)
{
return MyString();
}
char* tmp = new char[length + 1];
memcpy(tmp, this->s + position, length);
tmp[length] = STRING_ESCAPE;
MyString result(tmp);
delete[] tmp;
tmp = nullptr;
return result;
}
When I call this method and print the return value (I'm printing the char array actuallay, not the object itself), I receive complete garbage, which is carried out as a bunch of squares.
But when I return a temporary object return MyString(tmp), everything works fine. Initially i suspected this issue is associated to the delete[] operation, but commenting it out shows no effect.
The MyString constructor which is called is the following:
MyString::MyString(const char* s)
{
size_t length = this->strlen(s);
this->sLength = length;
this->s = new char[length + 1];
for (size_t i = 0; i <= length; ++i)
{
this->s[i] = *s;
++s;
}
}
So where is my mistake? Thank you!
In the two places you create MyString, they are being created within the context of your substr function. The return statement has to make a copy of them. Your copy constructor is probably not doing what you would need it to do.
A simpler design is for your substr function to return a pointer to a MyString that you create with operator new.
You were indeed missing the copy constructor. Something like this would do the work:
MyString::MyString(const MyString& other) {
size_t length = strlen(other.s);
s = new char[length+1];
strcpy(s,other.s);
this->sLength = length;
}
#include<iostream>
using namespace std;
class MyString{
private:
char *str=new char[10];
public:
MyString(){*str='\0';} //default constructor
MyString(char *s){ //parameterized constructor
str=s;
}
private:
int length(char* s){
int i=0;
while(s[i]!='\0')
i++;
return i;
}
char* delchar(char* s,int count,int start){
int i,j=0;
char *temp= new char[10];
for(i=start;i<start+count;i++){
s[i]=' ';
}
for(i=0;i<length(s);i++){
if(s[i]!=' ')
temp[j++]=s[i];
}
s=temp;
return s;
}
public:
MyString operator-(MyString s){
int i=0,j=0,count=0,start=-1;/* i to iterate the first string,j to iterate the second string*/
MyString temp; /* count to count the matched characters ,start to know the starting index*/
temp.str=str;
while(temp.str[i]!='\0'){
j=0;
start++;
while(s.str[j]!='\0'){
if(temp.str[i]==s.str[j]){
count++;
i++;
j++;
if(count==length(s.str)){//checks if the count
temp.str=delchar(temp.str,count,start);
i=i-count;
start=i-1;
count=0;
}
}
else{
i++;
count=0;
break;
}
}
}
return temp;
}
~MyString(){
delete str;
}
friend ostream &operator<<(ostream &stream,MyString& s){
stream<<s.str<<endl;
return stream;
}
};
int main(){
char *p= new char[20];
char *q= new char[10];
cin>>p;
cin>>q;
MyString s1(p);
MyString s2(q);
MyString s3;
s3=s1-s2;
cout<<s3;
delete p;
delete q;
return 0;
}
The above code overloads the - operator .It tries to subtract the substring from the main string for example input1:joshmathews input2:josh output:mathews. I am trying to store the output in a new MyString object s3. When I use a destructor as shown above,outputting s3
returns null. But when I don't use a destructor I get the expected output.Can anyone help?
The primary issue is that Operater- returns a local object which is copied by a default copy constructor -- that default copy constructor points s3 to the exact same memory/buffer of the temp MyString, and when that temp is destructed, it wipes out the memory s3 is using.
That is referred to as a dangling pointer. You can read more here: http://en.wikipedia.org/wiki/Dangling_pointer#Cause_of_wild_pointers
The code below is the changes I made to get your program executing and returning a valid result, while completing without error. To be clear though, there are issues with this altered version, runtime error or no runtime error, but this will help illuminate some important points.
When you define a type that allocates memory, you've really started to do somethings that don't come free. My altered version below completely got rid of the destructor, so actually it leaks memory up until the program ends. The destructor was invalid though, so removing it allowed the program the finish. But this would be one of those things that in general you shouldn't just accept.
I added a copy constructor and a copy assignment operator:
MyString(const MyString& s) {
strcpy_s(str, 10, s.str);
}
MyString& operator=(const MyString& s) {
strcpy_s(str, 10, s.str);
return *this;
}
Notice the strcpy_s in both of these. What is doing is copy the character string from the argument instead of just trying to point at the exact same string at the exact same address as the argument. If the argument gets destructed in your version, it wipes out some memory, so we can just accept default copy constructor and such since by default they are shallow copies that point to the same guts. That's one of the burdens of allocating memory -- you need to take care of that in both your destructor's ~and~ some of your constructors. This is referred to as "The Rule of Three":
If you need to explicitly declare either the destructor, copy constructor or copy assignment operator yourself, you probably need to explicitly declare all three of them.
Here's a wikipedia link about the rule of three: http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)
You needed a destructor, so that's the clue you need the others. The reason is as I was saying -- by default you get a copy constructor for free, but it just makes everything point to the same guts if there are points are resources, but then you can't delete one without affecting everybody else.
Also, in delchar I added this line towards the bottom:
temp[j] = '\0';
Characters strings must always end at a null, and you copied only actual letter characters into temp, so without a null character, a strcpy doesn't know where to end.
If you remember I yanked out your destructor, then those are the changes I made.
The destructor also has an issue, in that if you use new to create an array, like
char* c = new char[10];
then you need to delete it in a way that indicates in was new'd as an array, like so:
delete [] c;
The next thing you should look into is how your MyString construction happens. If you step through, you'll see that the str member get new'd -- that means it holds a valid address into a buffer you can use -- but then if the MyString(char *s) constructor is used, it literally just takes this new address of s, and makes str hold that address instead -- which means the old address pointing to the valid buffer is lost, and that buffer of new'd memory cannot be freed. So that's a leak. You could use the strcpy_s strategy from the constructors I added to copy the contexts of what is pointed to by the s in MyString(char *s) into the new'd buffer. That would make a huge positive difference in the code.
And don't bother about all the elitists out there -- many of them were born doing headstands, so they can't see to relate to newbies giving things an honest effort.
Here's the full altered code:
#include<iostream>
using namespace std;
class MyString{
private:
char *str = new char[10];
public:
MyString(){ *str = '\0'; } //default constructor
MyString(char *s){ //parameterized constructor
str = s;
}
MyString(const MyString& s) {
strcpy_s(str, 10, s.str);
}
MyString& operator=(const MyString& s) {
strcpy_s(str, 10, s.str);
return *this;
}
private:
int length(char* s){
int i = 0;
while (s[i] != '\0')
i++;
return i;
}
char* delchar(char* s, int count, int start){
int i, j = 0;
char *temp = new char[10];
for (i = start; i<start + count; i++){
s[i] = ' ';
}
for (i = 0; i<length(s); i++){
if (s[i] != ' ')
temp[j++] = s[i];
}
temp[j] = '\0';
s = temp;
return s;
}
public:
MyString operator-(MyString s){
int i = 0, j = 0, count = 0, start = -1;/* i to iterate the first string,j to iterate the second string*/
MyString temp; /* count to count the matched characters ,start to know the starting index*/
temp.str = str;
while (temp.str[i] != '\0'){
j = 0;
start++;
while (s.str[j] != '\0'){
if (temp.str[i] == s.str[j]){
count++;
i++;
j++;
if (count == length(s.str)){//checks if the count
temp.str = delchar(temp.str, count, start);
i = i - count;
start = i - 1;
count = 0;
}
}
else{
i++;
count = 0;
break;
}
}
}
return temp;
}
~MyString(){
//delete str;
}
friend ostream &operator<<(ostream &stream, MyString& s){
stream << s.str << endl;
return stream;
}
};
int main(){
char *p = new char[20];
char *q = new char[10];
cin >> p;
cin >> q;
MyString s1(p);
MyString s2(q);
MyString s3;
s3 = s1 - s2;
cout << s3;
delete p;
delete q;
return 0;
}
UPDATE: Allocated memory for str1's new data. Still memory error.
I'm trying to rewrite the += method for a string class I created.
Class mystring{
public:
friend void operator+=(mystring& str1, const mystring& str2){
mystring temp;
delete[] temp.data;
temp.length = str1.length + str2.length;
temp.data = new char[temp.length + 1];
strcpy(temp.data, str1.data);
strcat(temp.data, str2.data);
delete[] str1.data;
str1.length = temp.length;
strcpy(str1.data, temp.data);
}
private:
char *data;
int length;
}
Then in the main class:
mystring str1("hi");
mystring str2("matt");
str1 += str2;
cout << str1 << endl;
This function is working as it should be, but I am getting memory errors all over when I run valgrind. I am unable to figure out why that is. If anyone could give me any tips that would be awesome.
Thanks
Firstly, you meant not:
strcat(str1.data, str1.data);
but:
strcat(str1.data, str2.data);
Secondly, where do you expect str2.data to go? It is a memory scribble and hence the valgrind errors. Surprised it does not just crash.
You need to reallocate enough storage for the combined length, copy over both original strings and free str1.data before re-assigning it to the new storage.
Based on the updated post:
friend void operator+=(mystring& str1, const mystring& str2)
{
// Not using a temp mystring here, as the temp never really maintains its state as a mystring
// I am assuming length is the length of the string, not the storage. Not the best design if you consider resizing the the string to less than the storage
int newStringLength = str1.length + str2.length;
char* newStorage = new char[newStringLength + 1];
strcpy(newStorage, str1.data);
// strcat has to scan from the start of the string; we do not need to.
strcpy(newStorage + str1.length, str2.data);
delete[] str1.data;
str1.length = newStringLength ;
str1.data = newStorage;
// Haven't though about the case where str2 is an alias for str1.
}
You need to allocate additional memory in str1.
You can't just blindly copy past the end of the array.
You have to allocate heap to hold characters and release the heap when it is no longer needed.
Something like this:
data=new char[length+1];
//it is strange that operator += return void
// usually we have T& operator += (T const&, T const&)
//or T& T::operator +=(T const&)
friend void operator+=(mystring& str1, const mystring& str2){
//make sure str1 and str2 are correclty initialzed
str1.length = str1.length + str2.length;
//make sure str1.data has enough memory to hold all the data
//make sure str1.data and str2.data are null terminated strings, not binary data
strcat(str1.data, str2.data);
}