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 withdelete
(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);
}