I have created an object of a class inside a function. The created object is passed as a parameter to another class. I expect that when I exit the function in which the object is created, the object must be destroyed.
Also any references to this object must be invalid, but I find that the object referenced after exiting the function is still valid. Trying to understand the scope of the object. The code snipped is given below.
class TextWidget
{
public:
TextWidget()
{
cout << "Constructor TextWidget" << endl;
}
TextWidget(int id)
{
_ID = id;
cout << "Constructor TextWidget" << endl;
}
~TextWidget()
{
cout << "Destructor TextWidget" << endl;
}
void printWidgetInstance()
{
cout << this << endl;
}
int _ID;
};
class AnotherWidget
{
public:
AnotherWidget()
{
cout << "Constructor AnotherWidget" << endl;
}
~AnotherWidget()
{
cout << "Destructor AnotherWidget" << endl;
}
TextWidget* getTextWidget()
{
return _textWidget;
}
void setTextWidget(TextWidget* t)
{
_textWidget = t;
}
int getTextID() { return _textWidget->_ID; }
private:
TextWidget* _textWidget;
};
ButtonWidget b;
AnotherWidget a;
void fun()
{
TextWidget t(7);
b.setTextWidget(&t);
a.setTextWidget(&t);
a.getTextWidget()->printWidgetInstance();
b.getTextWidget()->printWidgetInstance();
}
int main()
{
fun();
cout << "TextWidget in AnotherWidget is ";
a.getTextWidget()->printWidgetInstance();
cout << "Text ID in a is " << a.getTextID() << endl;
getchar();
return 0;
}
OUTPUT
Constructor AnotherWidget
Constructor TextWidget
Before deleting TextWidget in class ButtonWidget 0x73fdf0
Before deleting TextWidget in class AnotherWidget 0x73fdf0
0x73fdf0
0x73fdf0
Destructor TextWidget
TextWidget in AnotherWidget is 0x73fdf0
Text ID in a is 7
A variable declared using automatic storage duration (not using new) has a lifetime of its scope. Accessing the variable outside the scope results in undefined behavior.
You need to understand what classes, objects, pointers and references are. Classes are code in memory, this code deals with the member variables. A class does not occupy any data memory. When you create a object you instantiate the class. This step will reserve some data memory for the local data of one object instance of the class. To access this instance you get a reference to the object (this). An object variable holds a pointer to the data memory of an instance of a class.
When an object gets destroyed the occupied memory block gets listed as free but not cleared. So if you still have a reference to that memory block you may access it. And as long as the system doesnt use this memory block for other purposes you still will find your bits and bytes there.
You may declare some variables:
long long_array[10];
myclass myobject;
These variables are stored sequential in one memory block. When you now access the memory behind the long_array through that array:
long_array[10] = 12345; // valid range is from long_array[0] to long_array[9]
Than you will overwrite the data of the object myobject.
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'm trying to learn C++, and from my understanding if a variable goes out of scope then it is destroyed and its memory is reallocated. If I have a class and it's method creates a variable, shouldn't that variable be destroyed after the method call? For example:
class TestClass {
public:
struct Pair{
std::string name;
int value;
};
void addPair() {
//should be deleted after push_back is called?
Pair x = Pair{ std::string{ "Test Object " }, counter++ };
pairs.push_back(x);
}
void printPairs() {
for (int i = 0; i < pairs.size(); i++) {
std::cout << "pair { " << pairs[i].name << " : " << pairs[i].value << " } " << std::endl;
}
}
void removePair() {
pairs.pop_back();
}
private:
int counter;
std::vector<Pair> pairs;
};
But when I tried addPair() then printPairs() then removePair() it works fine. Why doesn't it throw an error saying invalid access to memory location?
You said:
from my understanding if a variable goes out of scope then it is destroyed and its memory is reallocated.
That is correct. "reallocated" is not correct word I would use. I would phrase that as: The memory used by the object is available to be used by other objects.
And then you asked:
If I have a class and it's method creates a variable, shouldn't that variable be destroyed after the method call?
That is correct.
However, your situation is different.
When you use:
pairs.push_back(x);
a copy of x is placed in pairs. The copy continues to live in pairs after the function returns. Hence, printPairs() and removePair() work just fine.
First, access to variables out of scope is undefined behavior. The program might throw an error but it might even work well. So there's no guarantee an error will be raised.
Second, std::vector::push_back makes a copy of its arguments. So nothing to worry about when passing local variables to it.
As a new C++ hobbyist, I am still wrestling with understanding scope for class object created using "new". I thought I read that by instantiating a dynamic object using "new", the class (and members) would be accessible by all parts of the program directly (for example, within other functions) since it is not created on the stack.
In the code below, I set the initial value of 'tesValue1' to "111" in the constructor, and then try to update it to "222" in the 'check_accessibility' function. This fails to compile with the error "myGeneralVars" not declared within this [the function call] scope.
Any assistance is greatly appreciated.
//main.cpp
#include <iostream>
using namespace std;
class generalVars
{
private:
public:
//data attributes
int testValue1;
//constructor
generalVars()
{
testValue1= 111;
}
~generalVars(void)
{
cout << "destructor code here" << endl;
};
};
void check_accessibility() {
myGeneralVars->testValue1= 222;
}
int main(int argc, char *argv[])
{
generalVars* myGeneralVars = new generalVars; //create on heap
cout << "Main testvalue1 = " << myGeneralVars->testValue1 << endl; //value set in constructor
check_accessibility(); //sets testValue to new value
cout << "Main testvalue1 = " << myGeneralVars->testValue1 << endl; //want "222"
delete myGeneralVars; //delete from heap
return 0;
}
It has the lifetime you decide.
C++ uses the concept of storage duration: The lifetime of an object depends on where and how its instantiated. There are three (Four) types of storage durations:
Static storage duration: The object will be initialized at program start and will be destructed at program finish. Global variables and static class attributtes have static storage duration.
Automatic storage duration: The lifetime of an object is defined by the lifetime of the scope the variable is declared in. So the lifetime of local variables starts when the function starts, and ends when the function ends. The same for non-static class data members: Its lifetime starts when the object's lifetime starts, and ends when the lifetime of the object ends.
Dynamic storage duration: The object is created on dynamic memory using new and delete, and the lifetime is determined by the user. When the user does new the lifetime starts, and when does deletethe lifetime ends.
See this documentation page for more information.
In your case, you instantiate an object with dynamic storage duration, using a pointer variable to hold its memory address (The location of the object). Since you never do a delete on that pointer, the lifetime of the object never ends: Thats a memory leak.
Also, you try to access a variable defined in one function (main) in other function (check_accessibility). Of course that variable is not visible out of main. But thats nothing to do with dynamic storage duration, its only a simple name lookup problem. Whoever tells you storage duration has something related or depending on name/variable accessibility was completely wrong.
Since you're a hobbyist and a beginner, let's get you on the right track early so that you get more pleasure and enjoyment from your hobby.
consider the changes I have made here employing principles that will make your program more elegant, robust and pleasing as it grows in complexity.
//main.cpp
#include <iostream>
#include <memory>
using namespace std;
class generalVars
{
private:
public:
//data attributes
int testValue1;
//constructor
generalVars()
: testValue1(111)
{
}
~generalVars(void)
{
// if the class is carefully written using std:: objects
// you will rarely need to define a destructor
cout << "destructor code here" << endl;
};
};
// pass a reference to a mutable object to ensure that logic
// is performed on the object and not on its lifetime
void check_accessibility(generalVars& gv) {
gv.testValue1= 222;
}
int main(int argc, char *argv[])
{
unique_ptr<generalVars> myGeneralVars(new generalVars); //create on heap *safely*
cout << "Main testvalue1 = " << myGeneralVars->testValue1 << endl; //value set in constructor
check_accessibility(*myGeneralVars); //sets testValue to new value
cout << "Main testvalue1 = " << myGeneralVars->testValue1 << endl; //want "222"
// no delete necessary - unique_ptr's destructor takes care of it
return 0;
}
output:
Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1
Executing the program....
$demo
Main testvalue1 = 111
Main testvalue1 = 222
destructor code here
myGeneralVars is not known by your function check_accessibility(), so you cannot access the variable. Instead, try to pass it as parameter.
void check_accessibility(generalVars* genVar) {
genVar->testValue1= 222;
}
int main(int argc, char *argv[])
{
generalVars* myGeneralVars = new generalVars; //create on heap
cout << "Main testvalue1 = " << myGeneralVars->testValue1 << endl; //value set in constructor
check_accessibility(myGeneralVars); //sets testValue to new value
cout << "Main testvalue1 = " << myGeneralVars->testValue1 << endl; //want "222"
delete myGeneralVars; //delete from heap
return 0;
}
myGeneralVar is valid only inside the main() scope, so check_accessibility() has no access to it. You have to declare it before, either in global scope (outside function definition) or by passing it as argument to check_accessibility() inside main(). Keep in mind that the main function is not the global scope, as you might think. main() is just a function like others and has its own scope.
In function check_accessibility you use name myGeneralVars that was not yet declared.
void check_accessibility() {
myGeneralVars->testValue1= 222;
}
So the compiler issues athe error.
Before using any name it has to be declared.
You could write the function the following way
void check_accessibility( generalVars* myGeneralVars ) {
myGeneralVars->testValue1= 222;
}
and call it as
check_accessibility( myGeneralVars );
Also, you can define the check_accessibility() function inside generalVars class and call it from main as
mygeneralVars->check_accessibility();
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.