Why is the destructor called after assignment? - c++

Why is destructor being called here after the line mystring = "Hello, there!";
It is not like it is going out of scope yet. I am definitely missing some quirk of C++!
I know that line calls the default copy constructor, is the destructor always called after a copy constructor returns?
Just as a side note, I am using C++03, no C++11 yet.
EDIT : Also please note that I am aware of the double deletion that this below program causes. I am experimenting here. Just wanted to bring to your notice.
class MyString
{
private:
char* mystring;
int m_length;
public:
MyString(const char* str="")
{
assert(str);
m_length = strlen(str) + 1;
mystring = new char[m_length];
for (int i = 0; i < m_length; ++i)
mystring[i] = str[i];
mystring[m_length-1] = '\0';
}
~MyString()
{
delete[] mystring;
}
};
int main()
{
MyString mystring("");
mystring = "Hello, there!";
cout << "Destructor not yet called ";
}

Since you do not have a assignment operator for your class which takes a string literal mystring = "Hello, there!"; is turned into a 3 part operation.
First it has to construct a temporary object from the string literal.
Then it takes that temporary and uses it with the default copy(pre C++11)/move(post C++11 if not deleted otherwise) assignment operator the compiler generated for the class.
Then it has to destroy the temporary object that it created. That is why you would see a call to the destructor at the end of that expression.
Do note that since the temporary object is destroyed after
mystring = "Hello, there!";
That the pointer mystring holds is now deleted and you can no longer access it. It will also cause a double deletion when it is destroyed which is undefined behavior and will cause complications.

Related

Why is my r-value deleted after I have successfully assign it to an object? [duplicate]

This question already has answers here:
Guaranteed lifetime of temporary in C++?
(6 answers)
Closed 1 year ago.
When exactly is destructor being called? Initially, I thought that destructor were supposed to be called when we use the:
delete [] str;
Then, when I came accross the "move assignment" topic, I notice that the destructor is called directly after I NULL my rhs.str in my move assignment method.
main.cpp
int main() {
Mystring a{"Hello"}; // Overloaded constructor
a = Mystring{"Hola"}; // Overloaded constructor then move assignment
// cout << a.get_str() << endl;
a = "Bonjour"; // Overloaded constructor then move assignment
return 0;
}
Mystring.h
#ifndef _MYSTRING_H_
#define _MYSTRING_H_
class Mystring
{
private:
char *str; // pointer to a char[] that holds a C-style string
public:
Mystring(); // No-args constructor
Mystring(const char *s); // Overloaded constructor
Mystring(const Mystring &source); // Copy constructor
Mystring(Mystring &&source); // Move constructor
~Mystring(); // Destructor
Mystring &operator=(const Mystring &rhs); // Copy assignment
Mystring &operator=(Mystring &&rhs); // Move assignment
void display() const;
int get_length() const; // getters
const char *get_str() const;
};
#endif // _MYSTRING_H_
Mystring.cpp
// No-args constructor
Mystring::Mystring()
: str{nullptr} {
str = new char[1];
*str = '\0';
}
// Overloaded constructor
Mystring::Mystring(const char *s)
: str {nullptr} {
if (s==nullptr) {
str = new char[1];
*str = '\0';
} else {
str = new char[strlen(s)+1];
strcpy(str, s);
}
}
// Copy constructor
Mystring::Mystring(const Mystring &source)
: str{nullptr} {
str = new char[strlen(source.str)+ 1];
strcpy(str, source.str);
std::cout << "Copy constructor used" << std::endl;
}
// Move constructor
Mystring::Mystring(Mystring &&source)
: str(source.str)
{
source.str = nullptr;
std::cout << "Move constructor used" << std::endl;
}
// Destructor
Mystring::~Mystring()
{
if (str == nullptr) {
std::cout << "Calling destructor for Mystring : nullptr" << std::endl;
} else {
std::cout << "Calling destructor for Mystring : " << str << std::endl;
}
delete [] str;
}
// Copy assignment
Mystring &Mystring::operator=(const Mystring &rhs) {
std::cout << "Using copy assignment" << std::endl;
if (this == &rhs)
return *this;
delete [] str;
str = new char[strlen(rhs.str) + 1];
strcpy(str, rhs.str);
return *this;
}
//Move assignment
Mystring &Mystring::operator=(Mystring &&rhs) {
std::cout << "Using move assignment" << std::endl;
if (this == &rhs)
return *this;
delete [] this->str;
this->str = rhs.str;
rhs.str = nullptr;
return *this;
}
The output is as follow:
Using move assignment
Calling destructor for Mystring : nullptr
Using move assignment
Calling destructor for Mystring : nullptr
Calling destructor for Mystring : Bonjour
I have tried debugging but I just have no idea why it went into the Destructor method when I did not explicitly called the
delete [] str // or maybe
delete [] rhs.str
As you can see in the output, my r-value is deleted after every move assignment. I thought that every object should at least be deleted before the return 0;
When exactly is destructor being called?
Destructor is called when an object is destroyed of course. When that happens depends on the storage duration of the object.
I thought that destructor were supposed to be called when we use the:
delete [] str;
That's one case where an array of objects with dynamic storage duration is destroyed. That said, in the case of str those objects are of type char which have trivial destructors and thus there's nothing to "call" in practice.
int main() {
Mystring a{"Hello"};
The variable a has automatic storage duration. That object is destroyed at the end of the scope where it was declared (which is at the end of the function main). This is the destructor that you see last in the output.
a = Mystring{"Hola"};
There's a temporary object created here, used as the right hand operand of the assignment operator. The temporary object is destoyed at the end of the full-expression i.e. after the assignment. This is the destructor that you see first in the output.
a = "Bonjour";
This is same as the previously examined snippet, except the temporary object is less obvious because you're using an implicit converting constructor of the class. The destructor of this object is the one that you see between the first and the last in the output.
P.S.
#define _MYSTRING_H_
That name is reserved to the language implementation. By defining it, your program will have undefined behaviour. You should use another header guard that isn't reserved.
Move semantics do not change, in any way, the fundamental principles and rules of how and when objects get created and destroyed in C++. An object in automatic scope gets destroyed at the end of its automatic scope. An object in dynamic scope gets destroyed when it gets deleted. The End.
After a move constructor and/or move assignment operator gets defined for a class, its objects will continue to get constructed and destroyed at the same time, and place, where they were constructed and destroyed before move semantics were defined. This is not changed by move semantics.
All move semantics provide are an optimization path, to avoid unneeded work when copying objects effects a logical move.

Conversion constructor

Can any one tell me how the char* member gets deleted after conversion constructor is called.
The output is Debug Assertion Failed
class String
{
public:
char* m;
String()
{
m = NULL;
}
String(const char* str)
{
m = strdup(str);
}
String(const String& I)
{
m = strdup(I.m);
}
~String()
{
delete m;
}
};
void main()
{
String s;
s = "abc";
s = "bcd";
}
The problem is that you have not implemented an assignment operator. So when you do this
s = "abc";
you end up with two String objects (one of them a temporary) holding a pointer to the same address. They both try to delete the same object. You need to follow the rule of three.
Note: as #kolrabi has pointed out, you should be calling free on a pointer allocated with strdup, not delete.
Let's analyze s = "abc":
First of all, this is an assignment, not an instantiation, because s has already been declared.
So the compilation solution for this would be to create a temporary String object with "abc" as the argument to the String constructor, and then call the String assignment operator in order to copy that temporary object into s.
Now, since you have not implemented an assignment operator for this class, the default assignment operator is called, and simply copies each one of the member variables from the temporary String object into s.
Finally, the temporary String object is destroyed and the memory pointed by its m variable is deallocated. As a result, you end up with s.m pointing to an invalid piece of memory.

Why not only one? Copy constructor and assignment operator

I understand which is invoked in what situation...
Sample a;
Sample b = a; //calls copy constructor
Sample c;
c = a; //calls assignment operator
My question is Why these 2 different things exist at all? Why can't only one of the two take care of both situations?
No, they are different.
Copy constructor is used for constructing a new object (from another object). In this case you just need to initialize the members.
Assignment operator is used for an existing object (you may have constructed it by default constructor etc), and then assign it by another object. In this case you need to re-initialize members, sometimes means destroying and initializing them again.
Even so, the functionality of them are so similar, so you can share their implementation usually. Such as: What is the copy-and-swap idiom?
The copy constructor is invoked on creation, that means you don't have to take care of old resources in your object. The assignment operator on the other hand has to free the old resources. Besides they have different semantical meaning.
Suppose the String class:
class String
{
char *m_str;
size_t m_len, m_alloced;
public:
String(const char *str = "")
{
m_len = strlen(str);
m_alloced = m_len + 1;
m_str = (char *)malloc(m_alloced);
strcpy(m_str, str);
}
String(const String &other)
{
m_len = other.m_len;
m_alloced = m_len + 1;
m_str = (char *)malloc(m_alloced);
strcpy(m_str, other.m_str);
}
String &operator =(const String &rhs)
{
if (m_alloced < rhs.m_len + 1)
{
m_alloced = rhs.m_len + 1;
m_str = (char *)realloc(m_str, m_alloced);
}
m_len = rhs.m_len;
strcpy(m_str, rhs.m_str);
return *this;
}
const char *get() const
{
return m_str;
}
};
(live example)
Appearently, copy constructor and copy assignment operator does different things. Look at the String::String(const String &other).
m_len = other.m_len;
m_alloced = m_len + 1;
m_str = (char *)malloc(m_alloced);
strcpy(m_str, other.m_str);
It initializes its object. Set m_len, m_alloced, and m_str, and strcpy the string.
However, the String &String::operator =(const String &rhs) does -
if (m_alloced < rhs.m_len + 1)
{
m_alloced = rhs.m_len + 1;
m_str = (char *)realloc(m_str, m_alloced);
}
m_len = rhs.m_len;
strcpy(m_str, rhs.m_str);
return *this;
modify its object. Allocate more memory if required, and reset m_len and recopy the string.
Copy constructor is called when the object is created, and does initialize its object.
But copy assignment operator is called after the object is created, and does modify its object.
For example, look at this code.
String str1 = "asdf";
String str2 = str1;
String str3 = "12";
str3 = str1;
(it's also in the live example)
str1 is initialized by String::String(const char *). As you know, it'll contain "asdf".
str2 is initialized by copy constructor, String::String(const String &other). By the copy constructor, str2 will contain the same content.
str3 is initialized by String::String(const char *), like str1. However, in line 4, it is modified by copy assignment operator. So, str3 contains "12" at first, but its content will be modified into "asdf", by copy assignment operator.
Well, you could technically get away with just having a copy-constructor and a destructor, but that would require that every time you wanted to do an assignment you would have to destroy the object and then reconstruct it. This would become HIGHLY inefficient in the vast majority of use cases.
We cannot just have an operator= either, as you have no idea what the left-hand-side of the operator will be if it has not yet been constructed. It's members would probably contain garbage data and "shit would hit the fan".
Thus, you can think of the need for both an operator= and a copy-constructor as an optimization.
That being said there are a few design-patterns that can reduce the amount of code you have to write. For example, assuming you have a copy-constructor, a destructor and a swap-function implemented for your class. You could implement operator= using a copy-swap operation:
MyClass & operator=( MyClass rhs ) { // notice the call by value, this will
// implicitly call the copy-constructor.
swap(*this, rhs);
return *this;
}
Whilst this operator has strong exception safety, it is worth to notice that it may be much less efficient than reusing preallocated resources (which a more advanced operator= might do).
Basically, they are used in different situations, many correct answer, i just wanna add something to clear out where the copy constructor is also used:
For example:
void test(Fraction in){
//Do something here
}
Fraction test2(){
Fraction a(1, 2);
//Do something here
return a; // construct a return value by copy a
}
int main()
{
Fraction a(2, 3);
Fraction b(a); //construct b by copy a
Fraction c=a; //construct c by copy a
test(a); //construct in by copy a
}

Copy Constructor for char *data member using strcpy

I have doubt regarding copy constructor for a char* data member.
I have my code as,
#include<iostream>
using namespace std;
class Test
{
char *str;
int length;
public:
Test(){}
Test(char *a)
{
length = strlen(a)+1;
str = new char(length);
str = strncpy(str,a,length);
}
Test(const Test &t)
{
length = t.length;
if(t.str)
{
str = new char(length);
str = strncpy(str,t.str,length);
}
else
str = 0;
}
void out(){cout<<str;}
};
int main()
{
Test t("Test");
Test t1 = t;
t1.out();
return 0;
}
In case of constructor and copy constructor instead of using strncpy to copy the data member value, if I use:
Test(char *a)
{
str = new char();
str = a;
}
Test(const Test &t)
{
if(t.str)
{
str = new char();
str = t.str;
}
else
str = 0;
}
Will this also work? If yes, which method is preferable?
Not only the second version won't work, it will also provide you with a memory leak.
You first allocate memory to str, then you put in str the pointer pointing at the other object's string which means:
1. You no longer can access the memory you allocated.
2. Once you delete the string in one of the objects, it will be deleted in the other one too.
(*) you should also add an assignment operator.
In case of constructor and copy constructor instead of using strncpy to copy the data member value, if I use [code doing pointer copy]
I see that instead of strncpy you directly assign the pointer to the passed in string pointer. Will this work? No, what you're doing is just a pointer copy and not what the data the pointer is pointing to i.e. you need to deep copy and not shallow copy. You can use strncpy or std::copy_n.
Multiple other issues:
Use size_t instead of int to store sizes
Use constructor initilization list instead of assignment inside constructor body
Know the difference between new char [len] vs new char(); former allocates an array while the latter a char initialized to 0
Know that when you manage objects in free store, you've to implement an assignment operator, destructor, etc. a.k.a The Rule of Three/Five.
Allocating memory for a pointer and then losing its reference by allocating something else without releasing it will leak memory
Using std::string will be better since it's tried and tested

What happens to memory when I dereference a pointer and assign the value to some variable?

I have a class that has a private string pointer. This class also has a public method that dereferences that string pointer and returns the resultant string to the caller. My question is this: what happens to the memory that holds the dereferenced string?
Here's my code:
#include <iostream>
#include <string>
class myclass
{
private:
std::string *_name;
public:
myclass()
{
this->_name = new std::string("my name");
}
~myclass()
{
delete this->_name;
}
std::string getName()
{
return *this->_name;
}
};
void main()
{
myclass m;
{
std::string str = m.getName(); //what happens to this memory?
std::cout << str << std::endl;
} //str deleted here (I think)
//here, the m._name pointer hasn't yet been deleted
}
In my main function, the variable str gets assigned the dereferenced string. What happens to the memory that holds this string? I think that the memory is held on the stack and therefore gets "de-allocated" automatically when the variable goes out of scope. What confuses me is that the private string pointer may still be in scope even if the dereferenced string is no longer in scope. Does the str variable get assigned a copy of the private _name pointer's value?
If this code does copy the string in the background, should I be concerned about performance. Would my code run faster if I simply have getName() return the _name pointer without dereferencing it?
First off, you don't need a pointer. Second, you can return a copy (like you do) or a reference (or pointer if you want to stick with that), but there are pros and cons to either approach:
class myclass
{
private:
std::string _name;
public:
myclass() : _name("my name")
{
}
std::string getName1() { return _name; }
std::string& getName2() { return _name; }
const std::string& getName3() const { return _name; }
}
getName1 returns by value. The con is that a copy gets made, which can impact performance, but the returned value is safe to use after the object goes out of scope.
The difference between getName2 and getName3 is that you can use the former to modify the actual member of the class. The con is that the reference can no longer be used if the object goes out of scope, so you'd be left with a dangling reference. The pro is that it's more efficient, since there's no copying involved.
myclass x;
std::string a = x.getName1(); // copy is made
std::string& b = x.getName2(); // no copy
// b is mutable
b = "something else"; //this will modify x._name
const std::string& c = x.getName3; // no copy
// c is const
myclass* y = new myclass;
std::string d = x.getName1(); // copy is made
std::string& e = x.getName2(); // no copy
delete y;
d = "something else"; //legal
e = "something else"; //undefined behavior, e is a dangling reference
Follow the "rule of three". If you define any of: copy constructor, assignment operator, destructor, then define all of them. Then there will be no confusion whether the pointer to the string was deleted: you would just have a new one created in the corresponding assignment operator.