My friend told me C++ allows us to call a member function even if the instance is destroyed from memory. So I write the code below to verify it, but why the value of a can be extracted even after the object was destroyed? I thought there would be a segment fault.
#include <iostream>
class Foo{
public:
Foo(int a = 0){
std::cout << "created" << std::endl;
this->a = a;
}
~Foo(){
std::cout << "destroyed" << std::endl;
}
Foo *f(){
std::cout << "a=" << a << std::endl;
return this;
}
private:
int a;
};
Foo *iwanttocallf(int i){
return ((Foo)i).f();
}
int main(){
for(int i = 0; i < 3; i++)
iwanttocallf(i)->f();
}
Output from my Macbook Air:
created
a=0
destroyed
a=0
created
a=1
destroyed
a=1
created
a=2
destroyed
a=2
Usually compilers are implementing the member function call as a call to a regular c function with the first argument a pointer to the object (gcc does it like that as far as I know). Now if your pointer is pointing to one destroyed object it doesn't mean that the memory where the a has been stored will be changed, it might be changed. So it is undefined behavior in general. In your case you got a value of a but maybe next time with a different compiler or different code you will crash. Try to use placement new operator then set a value of 'a' = 0 in destructor... and follow the memory where the object is stored.
"My friend told me C++ allows us to call a member function even if the member is destroyed from memory"?
I don't know what your friend is trying to say. But you call member function on some object of a class
unless it's a static member. So, if you delete that object from memory, how could you call any function of that class on that object. It's an undefined behavior.
This is covered in ยง12.7 [class.cdtor]:
[..] For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor
finishes execution results in undefined behavior.
As other people have told, this involves undefined behavior, and any result is possible. I'll try to explain why you encountered this particular result (stuff working normally).
Objects in C++ are represented by contents of memory. When an object is destroyed, its
destructor is executed, but the memory still contains the previous value. The output operation outputs the value taken from memory (which is now "free" - doesn't belong to any object) - if there is not much stuff going on between the destructor call and the output, the old value will remain.
However, if you change your code to add some calculations, the bug will be evident. For example, I added the following function that simulates some calculations:
int do_stuff()
{
int result = 0;
int x[3] = {0};
for (auto& n: x)
{
n = rand();
result ^= n;
}
return result;
}
I also added a call to this function:
Foo *f(){
std::cout << "foo1: a=" << a << std::endl;
do_stuff();
std::cout << "foo2: a=" << a << std::endl;
return this;
}
I got the output:
foo1: a=0
foo2: a=424238335
This clearly shows that it's not safe to expect anything consistent when dealing with deleted objects.
By the way, some debuggers overwrite the memory that deleted objects occupied with a special value like 0xcdcdcdcd - to make some sense out of this kind of unpredictable behavior. If you execute your code under such a debugger, you will see garbage printed, and will immediately know that your code is buggy.
Related
This question already has answers here:
explicit call to destructor is not destroying my object why?
(7 answers)
Closed 4 years ago.
I want to explicitly call the destructor of an object in C++ to destroy that object.
This is just a simple program to experiment with the features of the programming languages. I have a default constructor which sets the internal data member to 1, and overloaded constructor which sets the internal data member to the parameter, and a destructor which displays the internal data member of the just destroyed object. There is also a function which prints the internal data member.
#include <iostream>
using std::cout;
using std::endl;
class myClass {
public:
myClass()
{
i = 1;
cout << "default ";
cout << "constructor called with " << this->i << endl;
}
myClass(int i)
{
this->i = i;
cout << "constructor called with " << this->i << endl;
}
~myClass()
{
cout << "object " << i << " destroyed!" << endl;
}
void printData()
{
cout << "object's data is " << i << endl;
}
private:
int i; // private data member
};
int main() {
myClass a;
myClass b;
myClass c(8);
a.printData();
b.printData();
c.printData();
/* I want to explicitly destroy object b. */
b.~myClass();
b.printData();
/* all the destructors get called when the objects go out of scope */
return 0;
}
My reasoning is this: I think that the destructor causes the object to be destroyed, so it no longer exists in the memory. After I explicitly call the destructor, I should be no longer able to use that object again, since it is destroyed. However, I am able to call a function of the object and print the value of the internal data member. Did manually calling the destructor fail to destroy the object? What's going on here?
Output:
default constructor called with 1
default constructor called with 1
constructor called with 8
object's data is 1
object's data is 1
object's data is 8
object 1 destroyed!
object's data is 1
object 8 destroyed!
object 1 destroyed!
object 1 destroyed!
Does calling the destructor destroy the object, or is calling the destructor a RESULT OF destroying the object?
After I explicitly call the destructor, I should be no longer able to use that object again, since it is destroyed
That's right. Only that it's C++ and instead of "you are no longer able" as in "you'll be warned when you do bad things", it's more like "you may be shot in the foot at any random point in the future without warning if you do bad things" (aka "undefined behavior").
In your particular example, you destroy the object, and it ceases to logically exist. The memory may still be allocated or even contain some old data. However, trying to access it is undefined behavior. It may "kind of work" on your specific combination of compiler, compiler flags, source code, OS and moon phase, it may work differently later, it technically may even wipe your hard drive.
Also, as you've declared the object in a local variable, it gets automatically destroyed second time when it goes out of the scope. That's also undefined behavior, but it does not really matter is your program's behavior is already completely undefined.
You may try catching such things with dynamical analysis like Valgrind, AddressSanitizer or UndefinedBehaviorSanitizer, but they provide no guarantees.
I am currently reading the second edition of C++: A Beginner's Guide by Herbert Schildt.
In Module 9.4, he talks about returning objects:
Just as objects can be passed to functions, functions can return objects. To return an object, first declare
the function as returning a class type. Second, return an object of that type using the normal return
statement. The following program has a member function called mkBigger( ). It returns an object that
gives val a value twice as large as the invoking object.
This is the 'following program' he mentions:
// Returning objects.
#include <iostream>
using namespace std;
class MyClass {
int val;
public:
// Normal Constructor.
MyClass(int i) {
val = i;
cout << "Inside constructor\n";
}
~MyClass() {
cout << "Destructing\n";
}
int getval() { return val; }
// Return an object.
MyClass mkBigger() {
Myclass o(val * 2); // mkBigger() returns a MyClass object.
return o;
}
};
void display(MyClass ob)
{
cout << ob.getval() << '\n';
}
int main()
{
cout << " Before Constructing a.\n";
MyClass a;
cout << "After constructing a.\n\n";
cout << "Before call to display.\n";
display(a);
cout << "After display() returns.\n\n";
cout << "Before call to mkBigger().\n";
a = a.mkBigger();
cout << "After mkBigger() returns.\n\n";
cout << "Before second call to display.\n";
display(a);
cout << "After display() returns.\n\n";
return 0;
}
This gives us the following output:
Before Constructing a.
Inside constructor
After constructing a.
Before call to display.
10
Destructing
After display() returns.
Before call to mkBigger()
Inside constructor
Destructing
Destructing
After mkBigger() returns.
Before second call to display.
20
Destructing
After display() returns.
Destructing
Schildt then goes on to explain that the reason there are two 'Destructing' messages during the mkBigger() call is because of the fact that:
when an object is returned by a function, a temporary object is automatically created, which holds the return value. It is this object that is actually returned by the function. After the value has been returned, this object is destroyed.
I was actually surprised there wasn't 3 'Destructing' messages. I have the following issue: Given the definition of mkBigger(), a new MyClass instance is created, and it is that instance that is returned and placed in the address of a. Thus, when doing
a = a.mkBigger();
My impression is thus that the original object previously held in a is no longer referenced by a. Is this correct? If so, I then have the following issues:
I was told C++ has some minute notions of garbage collection. Would that object thus be garbage-collected? where is this object now? Is this an example of the possible feared memory leaks that many mention when talking about the 'dangers' of C++?
One of the destructor in mkbigger() is called on o, the MyClass instance passed in by value; it goes out of scope at the end of the function. The other is called on the temporary copy of o returned when it is destroyed. What else goes out of scope? Not a in main(); therefore you should not expect a third destructor to be called. C++ does not provide garbage collection outside of calling destructors when automatic objects go out of scope.
Unlike some other modern languages, a does not "hold a reference" to an object; a is the object, in that it is a certain number of bytes holding the raw data members. When you do a = a.mkBigger();, MyClass's default assignment operator is called, which simply copies the val inside the temporary object on the right hand side into the val inside a, overwriting the value that was already there. a = a.makeBigger() would be equivalent to a.val = a.makeBigger().val if val were public.
Memory leaks occur when you use new to allocate memory and then fail to use delete to deallocate that memory. For classes that do this internally, you must write at least your own copy constructor, assignment operator, and destructor.
Why if I write a code like this (X is a generic class) calling constructor and destructor
int main()
{
X one ();
one.~X();
return 0;
}
give me this error?
Double free() or corruption:C++
You should almost never call destructors explicitly, they are called implicitly when an object falls out of scope (or deleted, if allocated on heap). First you manually deallocate background's array by calling background.~Y(), and next this same array tries to get deallocated once more at the next }.
Calling the destructor of an object is almost always wrong. Actually I never came across a situation where this was right. You need to read about RAII and maybe this example will help you to understand why you get the error:
#include <iostream>
struct Foo{
Foo() { std::cout << " constructor " << std::endl; }
~Foo() { std::cout << " destructor " << std::endl; }
};
int main(){
Foo foo;
// foo.~Foo(); // <- never ever do this !
} // <- object is destroyed here !
Run this and you will see, that the destructor is called already. Destroying an already destroyed object leads to the error you get.
Considering the following minimal code:
class MyClass {
public:
MyClass() {}
};
MyClass myfunc() {
MyClass obj;
cout << "Address of obj in myFunc " << &obj << endl;
return obj;
}
int main() {
MyClass obj(myfunc());
cout << "Address of obj in main " << &obj << endl;
return 0;
}
I obtain the following output:
Address of obj in myFunc 0x7fff345037df
Address of obj in main 0x7fff3450380f
Now, just by adding a destructor in MyClass, I get the following output:
Address of obj in myFunc 0x7fffb6aed7ef
Address of obj in main 0x7fffb6aed7ef
Showing that both objects are now the same... Is this just a coincidence ?!
Also, what does exactly happen in:
MyClass obj(myfunc());
I have overloaded the copy constructor to print a message, but it never appears...
By adding a destructor (whatever it was that you actually did, you're not showing the code) the behavior changed to use Return Value Optimization, known as RVO.
Then a pointer to the caller's storage is passed to the function, and the function constructs the object directly in that storage, instead of e.g. copying a value in a processor register or set of registers.
The same calling convention, with a hidden result storage pointer, can also be used without RVO. Without RVO a copy or move is performed at the end of the function. The standard supports RVO optimization under certain conditions, but, while it can be reasonably expected, a compiler is not under any obligation to perform RVO.
is it possible/ok to return a const reference even if the value the function returns is a local variable of this function? i know that locals are not valid anymore once the function returns - but what if the function is inlined and the returned value is only used within the callers scope? then the locals of the function should be included in the callers stackframe, no?
Don't count on it. Even if this works on 1 compiler, it's not standard supported behavior and is likely to break on others.
No, it's not OK. Local variables are declared on the stack, and the stack keeps changing between method calls. Also, the objects that get out of scope get destroyed. Always return a copy of a local variable.
Consider this code:
#include <iostream>
using namespace std;
class MyClass
{
public:
MyClass() { cout << "ctor" << endl; }
~MyClass() { cout << "dtor" << endl; }
MyClass(const MyClass& r) { cout << "copy" << endl; }
};
const MyClass& Test()
{
MyClass m;
return m;
}
int main()
{
cout << "before Test" << endl;
MyClass m = Test();
cout << "after Test" << endl;
}
This will print out:
before Test
ctor
dtor
copy
after Test
dtor
The object you're trying to copy has already called its destructor and may be in an invalid state.
inline is not a guarantee -- it's a suggestion. Even if you use tricks to force inline, you'll never be sure about the result, especially if you want to remain portable.
Hence, don't do it.
Doing that invokes undefined behaviour.
There's no way of forcing a compiler to inline the function. inline is just a suggestion - so is __forceinline
Even if you could guarantee that the function would be inlined, the destructor for the variable in question will still be executed, leaving you with a reference to a dead object.
And the big one - C++'s concept of the stack is delimited by scope - not by function.
#include <iostream>
int main()
{
{
int a = 5;
std::cout << std::hex << "0x" << &a << std::endl;
}
{
int b = 10;
std::cout << std::hex << "0x" << &b << std::endl;
}
}
My compiler puts 'a' and 'b' at different memory address. Except when I turn optimizations on. Yours may well decide that it's an optimization to reuse the memory your object previously occupied.
Is there a paticular problem you're trying to solve here? There are other ways of reducing the number of temporary objects created if that's your concern.
As others have noted, this is dangerous. It's also unnecessary, if your compiler supports the NRVO (Named Return Value Optimization), and your function uses and returns the local variable you would have liked to return by ref in a fairly simple way.
The NRVO allows the compiler to avoid copy construction under certain conditions - typically the main reason to avoid returning objects by value. VC++ 8 supports this (a delta on previous revisions) and it makes quite a bit of perf diff in frequently used code.
The value falls out of scope when the callee falls out of scope. So no, it is gone.
But if you want a fairly ugly solution (and a red flag warning you that your design might need refactoring), you can do something like this:
const MyObj& GetObj()
{
static const MyObj obj_;
return obj_;
}
...but this solution if fraught with peril, especially if the object is modifyable, or does something non-trivial in a multithreaded environment.
The inline keyword doesn't guarantee that the function is really inlined. Don't do it.