I recently started to learn C++, and one of the tasks of my "self-made-up program for making things in language clear to me" is develop a class which will have functionality similar to Java strings. So, it should be something like immutable strings, i.e. when you make changes on such a string, it should not change its internal chars array, but instead return a modified copy of itself. It's pretty straightforward, but I encountered some undestanding problems regarding memory management.
Things I already know about MM in C++:
objects which were created on the stack are disposed when they go out of scope
if an object is created in the heap (i.e. with new keyword), it should be disposed manually with delete (unless you use smart pointers.)
But if a class acts like immutable and, because of that fact, its objects implicitly create themselves sometimes, there are memory leak risks, as I can imagine. Here's an example (String is my educational class):
String *str1 = new String("str1"); // creating new object in the heap
String *str2 = new String("str2"); // another one
str1 = str2; // pointer to str1 object is lost, thereby it's memory leak
- or -
str1 += str2; // we haven't disposed old value of str1, now it's replaced by new object made of str1 + str2
Some implemetation code (not including headers, etc.) is below. All questions asked can be read in comments.
String.h
class String {
public:
explicit String(const char *);
String operator+(String);
String operator+(String *);
String operator=(String);
String operator=(String *);
String operator+=(String);
String operator+=(String *);
/* . . . */
private:
String() = default;
String(String *, String *);
byte *_contents;
int _length;
String.cpp
String::String(const char *original) {
int len = std::strlen(original);
this->_contents = new byte[len + 1];
std::strcpy((char *)this->_contents, original);
this->_length = len;
}
String::String(String *first, String *second) {
/* not so important implementation details */
this->_contents = new byte[capacity + 1];
/* more of not so important implementation details*/
}
String String::operator+(String another) {
return operator+(&another);
}
String String::operator+(String *another) {
return String(this, another);
}
// clang warning: "operator =() should always return String&",
// and if I correct that, then it says this operator should return only *this. Why?
String String::operator=(String replacement) {
return operator=(&replacement);
}
String String::operator=(String *replacement) {
// I guess, now I should dispose internal array, because one object is replaced by another. Right?
// Or, maybe, it should be "delete(this)"?
delete[](this->_contents);
this->_contents = replacement->_contents;
this->_length = replacement->_length;
this->_isWide = replacement->_isWide;
return *this;
}
String String::operator+=(String addition) {
return operator+=(&addition);
}
String String::operator+=(String *addition) {
auto concatenated = new String(this, addition);
// Same question here as above.
delete[](this->_contents);
return operator=(concatenated);
}
Related
I have a class that contains the operator + () to add 2 objects. The class contains an attribute char * variable str that points to an allocated char array from new char[] that contains a C style text string. I want the operator +() to concatenate two char arrays into a third char array allocated as a new buffer sized to contain the concatenated string as the result of the operator.
I am using overloaded constructor and initializing list and I initialize the variable to a default value. The destructor I've added to the class frees the memory allocated for the object text buffer in the char *str variable.
Everything works perfectly fine WITHOUT the class destructor, but as soon as I add the destructor, the program prints weird characters. I am also seeing a memory leak.
Here is the code and note that the class destructor is included here!
#include <iostream>
#include <cstring>
#include<cstring>
class Mystring {
private:
char *str;
public:
Mystring():
str{nullptr} {
str = new char[1];
*str = '\0';
}
Mystring(const char *s):
str {nullptr} {
if (s==nullptr) {
str = new char[1];
*str = '\0';
} else {
str = new char[std::strlen(s)+1];
std::strcpy(str, s);
}
}
Mystring(const Mystring &source):
str{nullptr} {
str = new char[std::strlen(source.str)+ 1];
std::strcpy(str, source.str);
std::cout << "Copy constructor used" << std::endl;
}
~Mystring(){
delete [] str;
}
void print() const{
std::cout << str << " : " << get_length() << std::endl;
}
int get_length() const{
return std::strlen(str);
}
const char *get_str() const{
return str;
}
Mystring&operator+(const Mystring &dstr)const;
};
// Concatenation
Mystring &Mystring::operator+(const Mystring &dstr)const {
char *buff = new char[std::strlen(str) + std::strlen(dstr.str) + 1];
std::strcpy(buff, str);
std::strcat(buff, dstr.str);
Mystring temp {buff};
delete [] buff;
return temp;
}
int main() {
Mystring m{"Kevin "};
Mystring m2{"is a stooge."};
Mystring m3;
m3=m+m2;
m3.print();
return 0;
}
Mystring &Mystring::operator+(const Mystring &dstr)const
This states that it's going to return a reference to a MyString Your temp however is local. This results in a far more serious issue than just a memory leak.
Once you're returning by value; you'll find that it might end up wanting to use the copy or move constructor; which leads to (as said in the comments) the rule of 3 (or rule of 5).
This
Mystring &Mystring::operator+(const Mystring &dstr)const {
char *buff = new char[std::strlen(str) + std::strlen(dstr.str) + 1];
std::strcpy(buff, str);
std::strcat(buff, dstr.str);
Mystring temp {buff};
delete [] buff;
return temp;
}
contains a bug. You create a temp object and return it by reference (Mystring&). That reference turns into a dangling one as soon as the function returns and using it is undefined behaviour. The fact that the code appears to work with or without the destructors means nothing. Undefined behaviour is undefined and you cannot expect anything from the code that invokes it.
You may simply change the return type of Mystring::operator+ to return by value (simply Mystring).
Although, you would encounter another problem later on.
m3=m+m2;
performs a copy assignment, which uses T& operator = (const T&). You did not provide one, so the compiler generated one.
Why is that bad? Because automatically generated special member functions perform shallow copies (if they are used for copying), which means that if you have, for example, pointer member variable, the pointer will be copied. Just the pointer. Not the data it points to. Just the pointer. You end up with two pointers pointing to the same memory and once a correctly defined destructor tries to free those resources, you encounter a double delete[] error, which, again, is undefined behaviour for non-nullptr pointers.
You may want to look forward into the rule of three, and then probably into the rule of five.
#include <iostream>
#include<cstring>
using namespace std;
class String
{
private:
char *sptr;
public:
String()
{
}
String(char str[])
{
sptr = new char[strlen(str) + 1];
strcpy( sptr, str );
}
String(const String& source)
{
sptr = new char[strlen(source.sptr) + 1];
strcpy( sptr, source.sptr);
}
~String()
{
delete sptr;
}
String operator=( const String& other )
{
if(&other!=NULL)
{
String tmp( other );
sptr = new char[strlen(tmp.sptr) + 1];
strcpy( sptr, tmp.sptr);
}
return *this;
}
void display()
{
for( char const* p = sptr; *p != '\0'; ++ p) {
std::cout << *p;
}
cout<<endl;
}
};
int main()
{
String a1;
String a2("Hello ");
String a3(a2);
a2.display();
a3.display();
//a2.change("Java ");
a2.display();
a3.display();
}
The Output pf the program is
Hello Hello Hello Hello.
But I am undone to do the following changes in my code... i.e.. In Main () I created the following objects
String a1;
String a2("Hello ");
String a3(a2);
a2.display();
a3.display();
//a2.change("Java ");
a2.display();
a3.display();
I want to change the the object a2(commented) with Java and desired the output as
Hello Hello Java Hello
through deep copy of the Data members through this pointers.
How can I change the Hello string to java
Your String class cannot be used to build a program upon, due to the faults within the String class. You must fix those errors first before even thinking about using String as a string class.
Therefore this answer addresses how to fix String first -- from there, you should now be able to write the program knowing you're not working on a faulty foundation.
Error 1: Default construction
You failed to initialize sptr when a String is default constructed. Thus when ~String() is executed, the call to delete will attempt to delete an uninitialized pointer.
Simple 1 line main functions such as this one:
int main()
{
String s;
} // <-- destructor of s may crash
will show undefined behavior and may crash, as shown by this example.
To fix this:
class String
{
public:
String() : sptr(nullptr) {}
//...
};
Now when delete [] is issued on sptr, no harm will occur since it is perfectly valid to delete [] a nullptr (it becomes basically a no-op).
Error 2: Faulty assignment operator
Your String::operator= fails to deallocate the previous memory that was allocated. In addition, the check for NULL is incorrect, since &other will always be true.
I am assuming this is the check for self-assignment, but it is not written correctly. The check for self-assignment is done by comparing the address of the current object (this) with the address of the object being passed in:
if (this != &other)
Once you have that, then you could have written the rest of the function this way:
if(this != &other)
{
String tmp( other );
std::swap(tmp.sptr, sptr);
}
return *this;
All this function does is copy other to a temporary String called tmp, swap out the sptr with the tmp.sptr and let tmp die off with the old data. This is called the copy / swap idiom, where you're just swapping out the contents of the current object with the contents of the temporary object, and letting the temporary object die off with the old contents.
Note that the check for self-assignment isn't necessary when using copy / swap, but there is no harm done in making the check (could even be a small optimization done when the check for self-assignment is present).
Edit: The other issue is that operator= should return a reference to the current object, not a brand new String object. The signature should be:
String& operator=( const String& other ) // <-- note the return type is String&
{
//... code
//...
return *this;
}
Error 3: Wrong form of delete
Since you allocated using new [], you should be using delete [], not just delete in the destructor:
~String()
{
delete [] sptr;
}
Given all of these changes, the following code no longer has issues, as shown by the Live Example here.
Now that you have a working String class, then you can build your application from there.
As to your program, you can easily change your string by using the assignment operator. There is no need for a change() function:
String a2("Hello ");
a2.display();
a2 = "Java ";
a2.display();
Since String::operator=(const String&) is no longer faulty with the changes above, assignment can now be done without issues.
I'm trying out a C# style string implementation in C++.
I have created an object and a pointer for class String, and assigned the object to the pointer. When i try to modify the object via the pointer instead of modifying the existing object i want to create a new object and make the pointer point it.
So i have overloaded the "=" operator, and creating a new object in the operator overloaded method. In order to reflect the change i need to use ss=*ss = "name";
Any suggestion to improve this code.
Below is my sample code
class String
{
char *str;
public:
String(char* str_in)
{
str = new char[strlen(str_in)];
strcpy(str, str_in);
}
String* operator=(char* s)
{
return new String(s);
}
};
int main()
{
String s="sample";
String *ss;
ss = &s;
ss=*ss = "name";
return 0;
}
I also tried to modify the this pointer, but not working as expected
String *ptr;
ptr = const_cast<String*>(this);
ptr = new String(s);
I would recommend some changes like this:
#include <string.h>
class String
{
char *str;
public:
String(const char* str_in)
{
str = new char[strlen(str_in)];
strcpy(str, str_in);
}
~String()
{
delete [] str;
}
String& operator=(const char* s)
{
char* tmp = new char[strlen(s)];
strcpy(tmp, s);
delete [] str;
str = tmp;
return *this;
}
};
int main()
{
String s("sample");
String *ss;
ss = &s;
ss = new String("name");
delete ss;
return 0;
}
First of all, you need an appropriate destructor or you are going to have a memory leak when String gets destroyed. Deleting the char* fixes this (since it is an array, we use the array delete).
Secondly, in C++, we almost always return a reference for operator= (not a pointer). So this revised operator= function is probably better - it deletes the old string, allocates memory for the new string, copies the new string, and returns *this.
Third, you can use const char* instead of char* for the constructor and assignment operator since you are not editing it.
In main(), I also created a new object for the pointer to point to since you requested that in the original post (and then it is deleted afterwards to avoid a memory leak).
Let me know if you have any questions with those changes.
Whenever a the String Destructor is hit it Triggers a Breakpoint, i think i might be deleting the same variable twice or not assigning the right amount of memory during the manipulation of m_str
#include "String.h"
#include <iostream>
using namespace std;
String::String()
{
m_str = nullptr;
}
String::String(const char* newStr)
{
m_str = new char[strlen(newStr)+ 1];
strcpy(m_str, newStr);
}
String::~String()
{
if (m_str != nullptr)
{
delete[] m_str;
}
}
void String::operator=(const String & myString)
{
if (m_str != nullptr)
{
delete[] m_str; //Breakpoint Apears Here
};
m_str = new char[strlen(myString.m_str) + 1];
m_str = myString.m_str;
}
void String::operator=(char* newStr)
{
if (m_str != nullptr)
{
delete[] m_str;
};
m_str = new char[strlen(newStr) + 1];
m_str = newStr;
}
}
Assigning one const char * to another as you do e.g. In the assignment operators doesn't copy the string, it just copies the pointer. So instead of two separate strings, you now have two pointers, pointing to the same string.
So for one, you are leaking the newly created char array and second, you are trying to delete whatever has been passed to the assignment operator, which in your case is probably a string literal instead of something that was created via new. Also in the case of the copy assignment operator, you would end up with two String objects pointing to the same char array and thus double deletion.
To solve this Problem, just use strcpy as you did in the constructor.
You correctly use a strcpy to copy chars from original char array in String::String(const char* newStr), but in all other places in your code, you wrongly write:
m_str = new char[strlen(myString.m_str) + 1];
m_str = myString.m_str;
First line correctly allocates an array of correct size, but the second erases the pointer obtained by new so:
you get a memory leak, since the allocated memory has no pointer on it any longer and will never be freed
the m_str member points to the original char array. If it is later deleted you have a dangling pointer, and if you delete this instance first, you will try to delete the original char pointer in another instance making it dangling, or even try to delete a static or local array.
TL/DR: consistently copy the character array with strcpy (or std::copy)
I have a class String with the following members:
Class String{
...
private:
char* word
int length
}
String's copy assignment returns a String& and allocates word on the heap.
I also have a linked list class written in C that has a function:
void *popFront(struct List *list)
{
struct Node prevHead = list->head
list->head = prevHead->next;
void *info = prevHead->info;
free(prevHead);
return info;
}
My task is to write the same function for a C++ linked list that uses the C function. This is what I have:
String List::popFront()
{
String s = (*(String *) ::popFront(&list));//casting the void * to String
return s;
}
The C++ method should return the object by value. That's what I thought the method would do. However,
I'm getting memory leaks. Any hint on how I need to modify List::popFront() so that it returns by value? Thanks.
First store the returned pointer locally as a pointer to string, then make a local copy of the string, then delete the object, the pointer points to and finally return the local copy.
EDIT:
Btw. you can use c++11's smart pointers to avoid the extra copy:
String List::popFront()
{
auto s = std::unique_ptr<String>((String*) ::popFront(&list));
return *s;
}
but that is probably not, what your teacher aims at.
popFront removes the element from the linked list and frees the node, but not the info portion. This is the responsibility of the caller of popFront, e.g.
String *s = (String *) ::popFront(&list);
delete s;
But you must also keep a copy, which you can return from the method
String List::popFront()
{
String *s = (String *) ::popFront(&list);
String tmp = *s;
delete s;
return tmp;
}
You declare String List::popFront() which returns an object, not a pointer. And String s = ... declares an object, not a pointer or reference. So assigning s a value constructs a new object s being a copy of what ::popFront returned, then a next copy of s is returned and s itself gets destroyed. However the object unlinked by ::popFront() remains on a heap, making a leak.
Don't create an intermediate copy of String object, return the pointer to unlinked object, so that a caller can link it to its own list or delete it after use:
String *List::popFront()
{
String *s = (String *) ::popFront(&list);//casting the void * to String *
return s;
}