The WTF behavior in class's dynamically allocated char array - c++

I crashed application every time I ran the code below. During debugging, I saw that the str object was self-destructing itself after the second line in main(). And it's quite a mystery for me. Try run it yourself:
#include<iostream>
class String {
private:
char* string = nullptr;
int length = 0;
public:
String(const char str[]) {
for (length; str[length] != '\0'; ++length);
string = new char[length + 1]{};
for (int i{}; i < length; ++i)
string[i] = str[i];
string[length] = '\0';
}
~String() {
delete[] string;
}
auto print() {
for (int i{}; i < length; ++i)
std::cout << string[i];
std::cout << '\n';
return *this;
}
auto getStrPtr() {
return string;
}
};
int main() {
String str("123");
auto strPtr{ str.print().getStrPtr() };
strPtr[0] = '3';
}
Am I missing something?
Note on line 2 in main(): I am trying to print the str's string array and then, since print() returns *this, I can chain methods and am calling a function that returns string's pointer. But, even if in debugger it all executes perfectly, I have no idea why the hell str object deconstructs itself after.

str object is not destroyed on the second line of main. The temporary copy of str is destroyed.
Temporaries are destroyed at the end of full expression. This is normal.
Your class deletes a pointer member in destructor, but fails to maintain class invariant of uniqueness of that pointer. The behaviour of the program is undefined when the destructor of str deletes the same pointer value as the destructor of the temporary object deletes. Behaviour is undefined even before that when strPtr[0] = '3'; indirects through the pointer that was deleted in the destructor.
Solution: Don't use owning bare pointers. Use smart pointer or container instead. std::string seems appropriate here.
But why that temporary object was created in the first place?
Because you return a String object in print. You initialise it with *this, and therefore that object is a copy. You don't use the returned object to initialise a variable, so a temporary object is materialised.

Related

operator(+) to concatenate text in char array results in memory leak

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.

C# style string implementation in c++

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.

Understanding the basics of copy on write

With regard to this line:
The s[9] that goes through void setChar(size_t index, char c), but what is *this (inside the if, in the line: ``)?
Is it s1 or nullptr? Then what is _str (in the same line: *this = _str;), is it s1?
class MyString {
size_t _size = 0;
char* _str = nullptr;
int* _pRefCount = nullptr;
}
operator char() const {
return ((const MyString&)_s)[_index];
}
};
void detach() {
if(_pRefCount && --*_pRefCount == 0) {
delete []_str;
delete _pRefCount;
}
}
void attach(const MyString& str) {
_size = str._size;
_str = str._str;
_pRefCount = str._pRefCount;
++*_pRefCount;
As always *this is the object the function is executed for. If you are inside s1[4] the *this would be s1.
Using *this = _str; is just an involved way of calling one of the operator= overloads for MyString. It has the effect of "unsharing" by assigning a new copy of _str to itself.
Also, it has been known for quite some time that reference counted strings is not an optimization. For example in the output of s1[0] the code will create an unnecessary CharProxy just in case someone would assign a value to it. You also get a separate memory allocation for the refcount of each string you create, even if you never intend to share it

Appending char pointer after shallow copying

I have made two object of string class each having char* pointer . By shallow copying, i have copied the first object into second object by shallow copying . Now both of them pointing at the same location.
What i have to do is append the char pointer through one object so that it does not make another but increase the size of original char pointer so second object point to the same location.
void String::append(char c) {
auto_ptr<StringBuffer> newdata(new StringBuffer);
newdata.get()->reserve(this->_str->length() + 1);
newdata.get()->smartCopy(this->_str);
this->_str = newdata.release();
this->_str->append(c);
}
The wrapper class of StringBuffer
void StringBuffer::reserve(int n) {
if (_length < n) {
int newlength = n; //max(_length*2,n);
char* newbuf = new char[newlength];
//copy contents of the stored string in the new buffer
revSmartCopy(newbuf);
//return stuff from the new buffer to the stored buffer
delete[] this->_strbuf;
this->_strbuf = newbuf;
this->_length = newlength;
newbuf = 0;
}
}
void StringBuffer::revSmartCopy(char* newString) {
int it = 0;
while (it < this->_length) {
newString[it] = this->_strbuf[it];
it++;
}
}
void StringBuffer::smartCopy(StringBuffer* newString) {
int shorterLength = 0;
(this->_length < newString->_length) ? shorterLength = this->_length : shorterLength = newString->_length;
int it = 0;
while (it < shorterLength) {
*_strbuf++ = *(newString->_strbuf)++;
it++;
}
}
This code is making another copying with object from whom we append pointing to new copy and older one pointing to previous
Let's assume you're doing this as an exercise, because it makes no sense otherwise.
You can't reallocate a pointer to a different size and have it at the same pointer value; this might happen accidentally, but it's impossible to enforce. Since the two objects are independent, the only way to make this work is double indirection - the pointer in your object points to a second pointer, which is the pointer to the character buffer.
You're also going to have a problem with destruction, because you have multiple objects with the same pointer. The standard library has std::shared_ptr to solve this very problem. If a pointer is shared between different objects, use shared_ptr to hold it.
Since there will only be one pointer to the actual character buffer, you can use std::unique_ptr for that one. You could use std::auto_ptr instead, and it will work fine as long as you don't try to copy it, but unique_ptr is a far better choice.

Assignment overloading: why does using the same object cause problems in the program?

Suppose we have the following:
class StringClass
{
public:
...
void someProcessing( );
...
StringClass& operator=(const StringClass& rtSide);
...
private:
char *a;//Dynamic array for characters in the string
int capacity;//size of dynamic array a
int length;//Number of characters in a
};
StringClass& StringClass::operator=(const StringClass& rtSide)
{
capacity = rtSide.capacity;
length = rtSide.length;
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i];
return *this;
}
My question is: why does this implementation of overloading the assignment operator cause problems when we try to assign an object to itself like:
StringClass s;
s = s;
The textbook I'm reading (Absolute C++) says that after delete [] a; "The pointer s.a is then undefined. The assignment operator has corrupted the object s and this run of the program is probably ruined."
Why has the operator corrupted s? If we're reinitalizing s.a right after we delete it, why does this cause such a problem in the program that we have to redefine the function as:
StringClass& StringClass::operator=(const StringClass& rtSide)
{
if (this == &rtSide)
//if the right side is the same as the left side
{
return *this;
}
else
{
capacity = rtSide.capacity;
length = rtSide.length;
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i];
return *this;
}
}
If you are assigning an object to itself both a and rt.a point to the same string, so when you do delete [] a you are deleting both what a and rt.a point to; then you do reallocate it, but the data you were going to copy (on itself) in the loop has been lost in the delete.
In the loop now you will just copy whatever junk happens to be in the memory returned by new on itself.
By the way, even with the "safety net" of the self-assignment check that assignment operator isn't completely ok (for instance, it's not exception safe); the "safe" way to define the "big three" (copy constructor, assignment operator, destructor) is using the "copy and swap idiom".
If you self-assign, you free (delete) the string via the LHS argument before you copy it to the newly allocated space via the RHS argument. This is not a recipe for happiness; it is undefined behaviour and anything may happen. A crash is plausible; if you're really unlucky, it may appear to work.
Consider what the value of rtSide.a is when you're inside the broken operator=.
It's the same as this->a, the values you just clobbered. Accessing non-owned memory is undefined behavior, thus accessing this->a is undefined behavior (since you just freed it).
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i]; //Invalid when this->a == rtSide.a
//because rtSide.a is no longer owned by your program.
If you did actually want to do this, you would have to make a copy of a before deleting it:
char* ca;
if (this == &rtSide) {
ca = copy of rtSide.a or this->a;
} else {
ca = rtSide.a;
}
//Do your assigning and whatnot
if (this == &rtSide) {
delete[] ca;
}
Obviously it's much more efficient to just do nothing instead of making temporary copies of all of an instances own members. It's the same concept as doing int x = 5; int y = x; x = y;
It is because you've first deleted the pointer delete [] a;
and then later on trying to copy from the deleted location:
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i]; //rtSide has already been deleted as 'this' and '&rtSide' are same.
Remember it is the same location you are trying to copy from, which you've already deleted.
Hence, the error!
The later code you posted fixes this problem by checking for self-assignment as a separate case.
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i];
That's why. Think of it like this:
You delete whatever a points to, then allocate a new chunk of memory. The new chunk of memory contains garbage which becomes your new data. Do not be confused by the loop that does a[i] = rtSide.a[i]; that only copies the garbage onto itself.
Remember, this and rtSide both lead you to the same object. When you modify the object using this the object that rtSide refers to is modified.