In the below code, when I pass an unnamed A variable to the ctor of B, the variable is destructed after the line. According to this answer :
Temporary objects are destroyed at the
end of the full expression they're
part of. A full expression is an
expression that isn't a sub-expression
of some other expression. Usually this
means it ends at the ; (or ) for if, while, switch etc.)denoting the end
of the statement.
I get it but how can the class B know the value of its mamber_a variable, after it is destructed? I know that copy ctor of A is enver called. How is this possible?
#include <iostream>
using namespace std;
class A
{
int sign;
A();
const A & operator=(const A &);
public:
A(int x) : sign(x) {
cout << "A ctor : " << sign << endl;
}
void WriteA() const {
cout << sign << endl;
}
~A() {
cout << "A dtor : " << sign << endl;
}
A(const A &) {
cout << "A copied : " << sign << endl;
}
};
class B
{
int sign;
const A & member_a;
public:
B(const A & aa , int ww ) : sign (ww) ,member_a(aa) {
cout << "B ctor : " << sign << endl;
}
void WriteB() const {
cout << "Value of member_a :";
member_a.WriteA();
}
~B() {
cout << "B dtor : " << sign << endl;
}
};
int main() {
A a(10);
B b1(a,1);
b1.WriteB();
B b2(A(20),2);
b2.WriteB();
return 0;
}
The output is :
A ctor : 10
B ctor : 1
Value of member_a :10
A ctor : 20
B ctor : 2
A dtor : 20
Value of member_a :20 // Object A was destructed. Where does this 20 come from?
B dtor : 2
B dtor : 1
A dtor : 10
You have one of the tricky parts of C++
It is pure chance that member_a has the value 20. You are hitting what is refereed to as undefined behavior.
When a class retains a reference to an external object it is the responsibility of the programmer to make sure that the lifetime of the object lasts longer than the object being referred to. In this case when you call member_a.WriteA(); the a object has already been destroyed and thus you are accessing memory that may potentially not belong to you (in this case it just happens to be in a location nearby that has not been overridden (completely by chance)).
If you are going to retain a reference to an object. You can retain a const reference but sometimes it is best to make the parameter a normal reference so that you can not accidentally pass a temporary value (this does not always work as you may have to pass a const object by reference).
Using a reference to a destructed object is an "undefined behaviour".
In A's destructor, try setting "sign" to "-1" and see what happens. Odds are the call to "WriteB" will show that you have sent a message to a deceased object.
Now try putting a bunch of stack using code between the constructor of b2 and the call to b2.WriteB, for example a call to a subroutine. You will probably now find that the call prints something different.
Related
#include <iostream>
#include <vector>
#include <algorithm>
class my {
public:
my() {
counter++;
std::cout << "class constructor" << counter << " \n";}
~my() {
std::cout << "class destructor" << counter << " \n";
counter--;
}
static inline int counter = 0;
};
int main()
{
my v1;
std::vector<my> my_vec;
my * p = new my();
my_vec.push_back(std::move(*p));
my_vec.push_back(std::move(v1));
}
simply example, however I do not understand what I am doing wrong, in result I get 2 extra destructor called than I expect (expect 2). Could some one explain it?
results:
class constructor1
class constructor2
class destructor2
class destructor1
class destructor0
class destructor-1
Analyzing the program step-by-step:
my v1;
One Instance is created, constructor is called.
my * p = new my();
Another instance is created, constructor is called.
my_vec.push_back(std::move(*p));
A copy of the second instance is inserted into the vector; the implicitly defined move-constructor is called (which just copies; no output is printed).
my_vec.push_back(std::move(v1));
The vector allocates new storage for 2 instances, copies the previously stored instance into the new storage (invoking the implicitly defined move-constructor, which just does copying, still no output for this), and invokes the destructor for the instance in the old storage (so first destructor output is printed).
Then, the vector goes out of scope, so its two contained elements get destroyed (so, 2 destructor calls). Then, v1 goes out of scope, printing the 4th destructor call. The instance p is leaked, i.e. never destroyed (memory leak).
Please see inline ...
#include <iostream>
#include <vector>
class my {
public:
my() {
counter++;
std::cout << "class constructor" << counter << " \n";
std::cout << "Object Address : " << this << std::endl;
}
~my() {
std::cout << "class destructor" << counter << " \n";
std::cout << "Object Address : " << this << std::endl;
counter--;
}
static inline int counter = 0;
};
int main()
{
my v1; /* 1. Object on stack (constructor called) */
std::vector<my> my_vec;
my * p = new my(); /* 2. Object created on heap (constructor called) */
my_vec.push_back(std::move(*p));
my_vec.push_back(std::move(v1));
}
Output is:
class constructor 1 Address : 0x7ffee1488760
class constructor 2 Address : 0x7f9f47400350
class destructor 2 Address : 0x7f9f474026e0
class destructor 1 Address : 0x7f9f474026f1
class destructor 0 Address : 0x7f9f474026f0
class destructor -1 Address : 0x7ffee1488760
Object on the stack called destructor as soon as it goes out of the scope i.e. Scope of the object on stack is limited to the code block. In this case block end when main exits.
But the object on heap (new) is a potential leak, had this being inside while loop would have consumed memory and eventually crashed. For each new you need to explicitly called delete, which means you have to call its destructor. Thanks to stack it does that for us but not heap, and c++ also doesn't have garbage collector.
std::move is a copy operation and hence no constructor is called. details here.
Take a look at this code:
#include <iostream>
using namespace std;
class A {
private:
int _x;
int _id;
static int count;
public:
A(int x) : _x(x) {
this->_id = A::count++;
cout << "Object with id " << this->_id
<< " has been created." << endl;
}
~A() {
cout << "Object with id " << this->_id
<< " has been destroyed." << endl;
}
int get_x(void) {
return this->_x;
}
A add(A& object) {
A tmp(this->_x + object._x);
return tmp;
}
};
int A::count = 1;
int main(void) {
A object_1(13);
A object_2(5);
A object_3(12);
object_3 = object_1.add(object_2);
cout << object_3.get_x() << endl;
return 0;
}
Here's the output from the program:
Object with id 1 has been created.
Object with id 2 has been created.
Object with id 3 has been created.
Object with id 4 has been created.
Object with id 4 has been destroyed.
18
Object with id 4 has been destroyed.
Object with id 2 has been destroyed.
Object with id 1 has been destroyed.
I don't understand what happened to Object with id 3? It definitely was created, but I see no line telling me that it was ever destroyed. Can you please tell me what's going on here?
As an aside question, why is it that when I use return 0, the destructors work fine, but when I use exit(EXIT_SUCCESS) I don't see Object with # has been destroyed printed on the screen as though the destructors are never called.
Is the previous object destroyed when the variable holding it gets assigned a new one using a copy constructor?
This question is moot because it is not possible to do so.
When you run
object_a = object_b;
this calls the assignment operator (not the copy constructor). It does not create or destroy any objects (unless your assignment operator does that).
In this case you haven't defined an assignment operator, so the default one is used, which overwrites object_3's ID with the other object's ID (which is 4). So when object_3 is destroyed it prints "Object with id 4 has been destroyed".
I have a class, let's call it A. There are two subclasses of class A which are a and b.
I'm making a pointer of class A like this:
A *pointer;
At some point in the program I initialize the pointer like this:
pointer = new a();
At some other point, I run a function of class A:
pointer->function(&pointer);
This function is inside class A (so all subclasses have it). There is a chance that when this function is called, I want to change pointer to another subclass, here is what I tried:
void A::function(A **pointer)
{
if (something)
{
delete *pointer;
*pointer = new b();
}
}
Although this works, I'm really curious if this is good practice, I'm calling delete from inside the object and freeing the object itself, could this be undefined behavior and I got lucky it worked? Am I not understanding this right? Am I making this more complicated than it should be?
Yes, that's valid as long as you are careful. See more discussion at a question specifically about delete this.
However, as with other things in C++ which are valid as long as you are careful, you are better to find another solution that will be less prone to errors. I suggest you reworking you code into a function returning a new pointer, and having the old one automatically destroyed (via a smart pointer, for example).
Something along the lines:
struct A {
static std::shared_ptr<A> function(std::shared_ptr<A>& ptr, int x) {
if (x > 0)
return std::make_shared<A>(x);
else return ptr;
}
A(int _x): x(_x) {}
int x;
};
Note also I made function() to be static, as it anyway accepts the object as its first argument. See live on coliru.
In fact, I don't quite like this solution with shared_ptr, and if someone will give a better implementation of this approach, I'll be glad to know.
This code is valid ( for more information about correctness see this answer ).
But it's not a good practice, because other developer can miss nuance, that using one of member functions will lead to reconstruction of object.
It's better to explicitly reconstruct object than hide it in member function.
Or just use smart pointers.
As a design I don't like that a pointer suddenly points to another object (of a different type) when it is not clear that it happens. It could be argued that since OPs code passes &pointer it indicates that it may change. However, I prefer an assignment instead - I think that is more clear.
I would try something like this:
int uglyGlobal = 1; // don't try this at home... ;-)
class A
{
public:
int n;
A() {n = uglyGlobal++; cout << "A cons for #" << n << endl;}
virtual ~A() {cout << "A des for #" << n << endl;}
unique_ptr<A> function(int something, unique_ptr<A> ptr);
};
class a : public A
{
public:
a() {cout << "a cons" << endl;}
~a() override {cout << "a des" << endl;}
};
class b : public A
{
public:
b() {cout << "b cons" << endl;}
~b() override {cout << "b des" << endl;}
};
unique_ptr<A> A::function(int something, unique_ptr<A> ptr)
{
if (something == 0)
{
// Turn it into an A
return unique_ptr<A>(new A);
}
else if (something == 1)
{
// Turn it into an a
return unique_ptr<A>(new a);
}
else if (something == 2)
{
// Turn it into an b
return unique_ptr<A>(new b);
}
else
// Keep the current
return ptr;
}
int main()
{
cout << "Make A" << endl;
unique_ptr<A> x (new A);
cout << "1. call - turn A into a" << endl;
x = x->function(1, move(x));
cout << "2. call - turn a into b" << endl;
x = x->function(2, move(x));
cout << "3. call - turn b into another b" << endl;
x = x->function(2, move(x));
cout << "4. call - keep current b" << endl;
x = x->function(3, move(x));
cout << "Return from main" << endl;
return 0;
}
The output is:
Make A
A cons for #1
1. call - turn A into a
A cons for #2
a cons
A des for #1
2. call - turn a into b
A cons for #3
b cons
a des
A des for #2
3. call - turn b into another b
A cons for #4
b cons
b des
A des for #3
4. call - keep current b
Return from main
b des
A des for #4
In general, You must ensure that every execution path that call function doesn't have a stack frame for a method of A or derived class (this is invalid).
So, it is dangerous. In MFC api programming, this happens "normally" in the WM_NCDESTROY message handler. In it you do thing like delete this, but Windows ensure that WM_NCDESTROY is the last message sent to a window.
I suggest you to change a little the api of class A and use unique_ptr to handle the memory:
#include <memory>
class A
{
public:
std::unique_ptr<A> f()
{
return std::make_unique<A>();
}
};
int main()
{
auto p = std::make_unique<A>();
p = std::move(p->f());
return 0;
}
In this way, you move the destruction from inside f() to the assignment of p.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am trying to understand the following code:
blueberry bitCopy(blueberry a) {
cout << "bitCopy " << blueberry::blQuantity << endl;
return a;
}
void howMany() {
cout << blueberry::blQuantity << endl;
}
int main() {
blueberry firstBl;
howMany();
bitCopy(firstBl);
howMany();
}
class blueberry:
#ifndef header_h
#define header_h
#pragma once
#include <string>
#include <iostream>
using namespace std;
class blueberry {
private:
static int blQuantity;
public:
blueberry();
~blueberry() {
blQuantity--;
}
friend blueberry bitCopy(blueberry a);
friend void howMany();
};
#endif
int blueberry::blQuantity = 0;
blueberry::blueberry() {
blQuantity++;
};
class blueberry is just some class that has a static int value blQuantity that increments in the constructor each time an object is created, and decrements in the destructor each time an object goes out of scope.
The read out from this program is:
1
bitCopy 1
-1
I was expecting 0 at the end rather than -1. Can someone explain this please?
PLEASE do not tell me I require a copy constructor. I am not trying to fix this code so that the object count works. I am instead trying to understand how this works and why the blQuantity is not the value I expect.
class blueberry is just some class that has a static int value blQuantity that increments in the constructor each time an object is created, and decrements in the destructor each time an object goes out of scope.
Are you sure that's each and every time one is created? I think there is something you've missed.
blueberry bitCopy(blueberry a)
That's pass-by-value; i.e., blueberry a here is a copy of what was submitted to bitCopy(). That invokes blueberry's copy constructor, which you have not defined. The compiler thus creates a simple one for you, which copies over any member values from the original object -- but it does NOT increment anything. If you want that, you'll have to define:
blueberry::blueberry (const blueberry&) // copy constructor
blueberry& operator= (const blueberry&) // copy assignment operator
You may also want a move constructor and move assignment operator -- see that wikipedia article about the "rule of three (or five)"
I linked in the above paragraph.
The reason blQuantity is -1 at the end is because there are actually two copies made with bitCopy(), one for the parameter and one for the return value. If you change it to:
blueberry bitCopy (blueberry &a)
I.e., using pass-by-reference, there will only be one copy and blQuantity will be 0 afterward. If you then make the return value void, there will be no copies made and blQuantity should be 1.
Here's a demonstration of the roles of the copy constructor and operator= (copy assignment operator):
#include <iostream>
#include <string>
using namespace std;
class A {
public:
string x;
A (string s) : x(s) {
cout << "A con " << "(" << x << ")\n";
}
A (const A& other) : x(other.x) {
x.append("-copy");
cout << "A copy " << "(" << x << ")\n";
}
A& operator= (const A& other) {
x = other.x;
x.append("[=]");
cout << "A assign " << "(" << x << ")\n";
return *this;
}
~A () { cerr << x << " A bye!\n"; }
};
A test (A a) {
return a;
}
int main (void) {
A a("#1");
cout << "test()\n";
A b = test(a);
cout << "Copy assign:\n";
b = a;
cout << "Exiting...\n";
return 0;
}
I'll step through the output from this:
A con (#1)
test()
A copy (#1-copy)
A copy (#1-copy-copy)
#1-copy A bye!
The first line is from A a("#1"). The last three lines are a result of A b = test(a). The first one is copying in the parameter, A test (test a). The second is the creation of the return value, which is a copy of the parameter, so the tag on the object is now #1-copy-copy. That initializes b in main(). When test() exits, the parameter object is destroyed, #1-copy A bye!.
Copy assign:
A assign (#1[=])
This is from b = a in main(). Notice that the previous version of b is not destroyed. This is because copy assignment is meant to turn one object into a copy of another; neither object is destroyed, but the contents of the target object is presumably changed, hence b's tag is now #1[=]. So predictably:
Exiting...
#1[=] A bye!
#1 A bye!
When the program ends, a and b are destroyed.
If you change the signature of test() to:
A& test (A &a)
You'll get this output:
A con (#1)
test()
A copy (#1-copy)
Copy assign:
A assign (#1[=])
Exiting...
#1[=] A bye!
#1 A bye!
Only two objects are ever created, a via the constructor and b via the copy con; both of them are not destroyed until the end. If you then do not use the return value of test(), only one object is ever created.
class Base
{
private:
int nID;
friend int fnDeleteBase(Base* base);
public:
Base( int baseID):nID(baseID) { cout << "Base Constructed with value" << endl; }
Base () : nID(5){cout << "Base Constructed WITHOUT value" << endl; }
~Base() { cout << "Base class object killed " << endl; }
};
int fnDeleteBase(Base* base) // Line 1
{
delete base; // Line 2 - important
cout << "Base object deleted " << endl;
return (1);
}
int main()
{
Base aBase; // Line 3
try
{
int i = fnDeleteBase(&aBase); // Line 4
}
catch(...)
{
cout << "Exception handled " << endl;
}
return (0);
}
The above code snippet I was debugging. I am unable to Step into at Line 2 where I am deleting the base object. as soon as I try to step into or run over the line 2, the control goes and I have to kill the debugging or execution
The output I get is:
Base Constructed (any of the construction is valid)
Base class Object killed
However everything works fine and if Line 3 is changed to Base * aBase = new Base();. The output on console is:
Base Constructed (any of the construction is valid)
Base class Object killed
Base object Deleted
Can somebody share the technical details behind the two?
You should only use delete on pointers that were constructed using new (or pointers that were assigned to other pointers that were constructed using new), no exceptions (that I know of).
Using delete on anything else (which is what happens in the first case, since the parameter of fnDeleteBase is a pointer to Base aBase, which is not a pointer and there is no new keyword there) results in undefined behaviour.
Explaining what exactly the compiler does won't really be helpful as, with undefined behaviour, this can differ from compiler to compiler. And it should be avoided at all costs.