I have read in C++ : The Complete Reference book the following
Even though objects are passed to functions by means of the normal
call-by-value parameter passing mechanism, which, in theory, protects
and insulates the calling argument, it is still possible for a side
effect to occur that may affect, or even damage, the object used as an
argument. For example, if an object used as an argument allocates
memory and frees that memory when it is destroyed, then its local copy
inside the function will free the same memory when its destructor is
called. This will leave the original object damaged and effectively
useless.
I do not really understand how the side effect occurs. Could anybody help me understand this with an example ?
Here is an example:
class bad_design
{
public:
bad_design( std::size_t size )
: _buffer( new char[ size ] )
{}
~bad_design()
{
delete[] _buffer;
}
private:
char* _buffer;
};
Note that the class has a constructor and a destructor to handle the _buffer resource. It would also need a proper copy-constructor and assignment operator, but is such a bad design that it wasn't added. The compiler will fill those with the default implementation, that just copies the pointer _buffer.
When calling a function:
void f( bad_design havoc ){ ... }
the copy constructor of bad_design is invoked, which will create a new object pointing to the same buffer than the one passed as an argument. As the function returns, the local copy destructor will be invoked which will delete the resources pointed by the variable used as an argument. Note that the same thing happens when doing any copy construction:
bad_design first( 512 );
bad_design error( first );
bad_design error_as_well = first;
That passage is probably talking about this situation:
class A {
int *p;
public:
A () : p(new int[100]) {}
// default copy constructor and assignment
~A() { delete[] p; }
};
Now A object is used as pass by value:
void bar(A copy)
{
// do something
// copy.~A() called which deallocates copy.p
}
void foo ()
{
A a; // a.p is allocated
bar(a); // a.p was shallow copied and deallocated at the end of 'bar()'
// again a.~A() is called and a.p is deallocated ... undefined behavior
}
Here is another example. The point is that when the callee (SomeFunc) parameter destructor is invoked it will free the same object (ptr) pointed to by the caller argument (obj1). Consequently, any use of the caller argument (obj1) after the invocation will produce a segfault.
#include <iostream>
using namespace std;
class Foo {
public:
int *ptr;
Foo (int i) {
ptr = new int(i);
}
~Foo() {
cout << "Destructor called.\n" << endl;
//delete ptr;
}
int PrintVal() {
cout << *ptr;
return *ptr;
}
};
void SomeFunc(Foo obj2) {
int x = obj2.PrintVal();
} // here obj2 destructor will be invoked and it will free the "ptr" pointer.
int main() {
Foo obj1 = 15;
SomeFunc(obj1);
// at this point the "ptr" pointer is already gone.
obj1.PrintVal();
}
Related
I am pretty new to the C++ world...
I have a question about destructors in C++.
If there is a class like:
class TestClass {
public:
double *array;
}
When I put a pointer of one array to *array and put again another and delete Testclass instance, then I can no longer access the older one?
It becomes an error:
int main() {
TestClass tc;
tc.array = new double[10];
double *array2 = tc.array;
tc.array[3]=9;
tc.array = new double[10];
delete &tc; // <------------------- this one.
std::cout<<array2[3]<<std::endl;
// std::cout<<tc.array[3]<<array2[3]<<std::endl;
}
If there is no delete &tc and activate the commentline (the last line),
'09' shows up, (tc.array[3] != array2[3] => tc.array != array2)
which does not mean tc.arrray is not the one in *array2 ??
What is wrong?
The tc is a function-scoped variable (allocated on the stack). It's not allocated with new. You can't delete it. it will be freed automatically when the function return
Dealing with raw pointers is somewhat tricky and your class would probably benefit from using a std::vector<double> instead of a raw pointer. Another choice could be a std::unique_ptr<double[]> that handles deleting the pointer for you.
That said, if you want to try it out, you should know when and what to delete.
tc is an automatic variable. You didn't create tc using new so you should not delete it. It will be destroyed when it goes out of scope - when the program leaves the block in which tc was created.
You haven't defined a destructor for TestClass so the memory to which array points will still be allocated even after tc is destroyed. The destructor would typically look like this:
~TestClass() {
delete[] array;
}
You also assign directly to tcs internal pointer, which is dangerous. You should have just as many delete[] as you have new[]s, which is not the case in your program, so it'll leak. You should typically hide the internal pointer so it can't be reassigned without the object owning it having control.
For manual memory management to work properly, there are some member functions you should consider:
Copy constructor
Move constructor
Copy assignment operator
Move assignment operator
Destructor
You can read more about those here: The rule of three/five/zero
Here's an example of what your class could look like with the 5 member functions mentioned above plus a default constructor, a converting constructor taking a pointer as parameter and two subscript operators.
#include <iostream>
#include <utility> // std::move, std::swap, std::exchange
class TestClass {
public:
// default constructor
TestClass() :
array(nullptr) // member initializer
{
std::cout << "default ctor\n";
}
// converting constructor - refuse lvalue pointers since we want to make sure
// that we "own" the pointer
TestClass(double*&& p) : array(p) {
std::cout << "converting ctor " << p << "\n";
}
// copy constructing without knowledge about the size of the array isn't possible
TestClass(const TestClass&) = delete;
// move construction works though
TestClass(TestClass&& rhs) :
array(std::exchange(rhs.array, nullptr)) // steal pointer from rhs
{
std::cout << "move ctor\n";
}
// copy assignment without knowledge about the size of the array isn't possible
TestClass& operator=(const TestClass&) = delete;
// move assignment works though
TestClass& operator=(TestClass&& rhs) {
std::cout << "move assignment\n";
// swap pointers with rhs - let rhs delete[] our current pointer
std::swap(array, rhs.array);
return *this;
}
~TestClass() { // destructor
delete[] array;
}
// subscripting support
double& operator[](size_t idx) { return array[idx]; }
double operator[](size_t idx) const { return array[idx]; }
private: // hide your raw pointer from direct access
double* array;
};
void printer(const TestClass& tc, size_t idx) {
// use const version of operator[] in the class
std::cout << tc[idx] << "\n";
}
int main() {
TestClass tc; // default ctor
// use the converting constructor that creates a TestClass object from a pointer.
// It is then move assigned to tc
tc = new double[10];
tc[3] = 1; // use the non-const version of operator[] in the class
printer(tc, 3);
double* a = new double[10];
// tc = a; // this won't work since we don't accept lvalues in assignment
tc = std::move(a); // make an xvalue to allow the assignment. "a" should not be
// delete[]-ed after this since we granted tc the possibillity
// to take ownership the pointer.
tc[3] = 2;
printer(tc, 3);
}
Maybe the question is basic but I didn't found answer for that.
I will give the some examples (when Object has destructor), and I will glad to understand what happens in each one:
1)
int f (){
Object *p=new Object();
int something=5;
return something;
}
I think it would not call the destructor, but someone told me that if the function has return then the destructor will be called.
2)
Object & f (){
Object *p=new Object();
return *p;
}
How about now? is it connected to the object instance we return?
if 1) is not calling detractor please confirm that 3) is not calling the destructor (I still think it will not, but confirming)
3)
int f (){
for(int i=0; i<10; i++){
Object *p=new Object();
}
int something=5;
return something;
}
If 1) is calling the destructor: if 3) was void function, does the destructor
will be called (I again think not).
In case #1, the destructor is NOT called, because the object exists in dynamic (heap) memory, so it will not be destructed until delete is called on it. Every new needs a matching delete.
The destructor WOULD be called if the object were created in automatic (stack) memory instead, eg:
int f() {
Object p; // <-- no 'new'
int something=5;
return something;
} // <-- ~Object() is called here
In case #2, the destructor of the object pointed to by p is NOT called, and the return statement is returning a reference to the same object that exists in dynamic memory.
If the function returned an Object by value instead of by reference, then the return statement would make a copy, and the copy WOULD be destructed (but not the original!) when the caller is done using the returned object, eg:
Object f() {
Object *p=new Object();
return *p; // <-- copy made here
}
f(); // <-- ~Object() for the return value
// is called when the ';' is reached
In case #3, again the objects being created in dynamic memory are NOT destructed automatically upon return.
In all three cases, you can use std::unique_ptr to provide automatic memory management of dynamic objects, eg:
int f() {
std::unique_ptr<Object> p(new Object);
int something=5;
return something;
} // <-- ~unique_ptr() is called here
std::unique_ptr<Object> f() {
std::unique_ptr<Object> p(new Object);
return std::move(p);
} // <-- ~unique_ptr() for p is called here
f(); // <-- ~unique_ptr() for the return value
// is called when the ';' is reached
int f() {
for(int i=0; i<10; i++) {
std::unique_ptr<Object> p(new Object);
} // <-- ~unique_ptr() is called here
int something=5;
return something;
}
Absolutely not. C++ is not garbage-collected like Java.
Every new has to be balanced with a delete, and you need to pass the pointer you get back from new to that call to delete. (More formally that pointer has to be the same type too, or polymorphically related).
Fortunately, C++ does provide smart pointer classes like std::unique_ptr that wrap the delete call in its destructor.
Below is the code which has various return statements and all are working perfectly fine.
Compiler throws the warning for fun_ret_obj1
Test.cpp: In function ‘myClass& fun_ret_obj1()’:
Test.cpp:45: warning: reference to local variable ‘myObj’ returned
But still the output seems to be fine.Is it by Chance?
Are there any catches with any of the return statements below?
Explanation would be really helpful, Thanks
#include <iostream>
using namespace std;
class myClass {
public:
int a ;
myClass()
{
a = 10;
}
};
myClass& fun_ret_add()
{
myClass *ptr = new myClass();
return *ptr;
}
myClass* fun_ret_ptr()
{
myClass *ptr = new myClass();
return ptr ;
}
myClass fun_ret_obj()
{
myClass myObj;
return myObj;
}
myClass& fun_ret_obj1()
{
myClass myObj;
return myObj;
}
int main()
{
myClass obj,obj1;
std::cout <<"In Main \n";
myClass *a = fun_ret_ptr();
std::cout<<a->a<<"\n";
myClass &b = fun_ret_add();
std::cout<<b.a<<"\n";
myClass c = fun_ret_obj();
std::cout<<c.a<<"\n";
myClass d = fun_ret_obj1();
std::cout<<d.a<<"\n";
}
First one is a memory leak:
myClass& fun_ret_add()
{
myClass *ptr = new myClass();
return *ptr;
}
Second one returns a raw pointer (evil - return a std::unique_ptr)
myClass* fun_ret_ptr()
{
myClass *ptr = new myClass();
return ptr ;
}
Third one is perfect - returns a copy, which will almost always be elided. In c++17 it's guaranteed to be elided. This is efficient and safe.
myClass fun_ret_obj()
{
myClass myObj;
return myObj;
}
update
In c++17 you could guarantee the elision of the copy this way:
myClass fun_ret_obj()
{
return myClass{};
}
end of update
Fourth one is undefined behaviour. Returning a reference to a non-existent object. Never do this.
myClass& fun_ret_obj1()
{
myClass myObj;
return myObj;
}
regarding memory leaks
It is true that in the first example, a caller could release the memory if he/she knew that myClass had been allocated with new:
auto& x = fun_ret_add(); // a
...
delete std::addressof(x); // b
This would require:
That the caller knows that fun_ret_add() is implemented in terms of new.
That the implementation of fun_ret_add() never changes
That no exceptions occur between (a) and (b)
The second example is similar. In this case, at least there is a hint that the object needs to be deleted, but the caller must still know that the object has been allocated with new, and he must guard against exceptions.
Contrast with this:
std::unique_ptr<myClass> fun_ret_ptr()
{
return std::make_unique<myClass>();
// or
return { new myClass() };
// or
return std::unique_ptr<myClass>(new myClass());
}
Now the caller receives a smart pointer. If the caller does nothing but use this pointer, the myClass object will be properly deleted when the pointer goes out of scope, and all memory will be reclaimed.
myClass& fun_ret_obj1()
{
myClass myObj;
return myObj;
}
This creates a local variable on the stack, myObj. And returns a reference to that object. Then the object is destroyed because of it's scope. The moment the caller sees the reference it's referencing a stack object that has been destroyed, using it is undefined behaviour. And thus your compiler warns you about that.
OK some explanations:
myClass fun_ret_obj()
{
myClass myObj;
return myObj;
}
This one simply calls a copy-constructor. Nothing really special here.
myClass* fun_ret_ptr()
{
myClass *ptr = new myClass();
return ptr ;
}
This one returns a pointer to a heap allocated object. It will never be deleted until you manually delete it. But it's safe to return.
myClass& fun_ret_add()
{
myClass *ptr = new myClass();
return *ptr;
}
This one will return a reference to the value. While this is OK. You can not access the pointer ptr anymore and thus not delete the object manually. (OK you still could delete the object but you have to know later that this object was initially created on the heap and not on the stack to not cause any strange errors elsewhere. So this is likely to not get deleted later)
myClass& fun_ret_obj1()
{
myClass myObj;
return myObj;
}
This one is critical. The moment the function goes out of scope the destructor will be called. (if you set a to a invalid value in the destructor you will see this).
Because the computer is "smart" and just says "this memory can be overwritten when needed" it gets not "deleted" (the destructor gets called though just not the memory location invalidated). So directly accessing the memory afterwards results in what seems like valid behavior. But this is just by accident. When you would initialize some variables or have some memory allocations on the stack this would get overwritten and you access strange memory. That's why this is undefined behavior and the compiler warns you here.
It's by chance. Your compiler doesn't lie.
Here:
myClass& fun_ret_obj1() {
myClass myObj;
return myObj;
}
You are returning a reference to an object that is going to be destroyed.
You are going to face what is called undefined behavior, it could either works or not.
In your case:
the output seems to be fine
And it's by chance, of course.
myClass fun_ret_obj()
{
myClass myObj;
return myObj;
}
Looks innocent enough, but under the hood will create two temporaries (invoking the ctor on each, which propagates to ctors of all member objects, and so on...). Then when returning will copy the first temporary to the other before deleting it (invokes its dtor and the dtor of all member objects, and so on...), will then invoke the copy ctor (or assignment operator and those of its member objects, and so on...) for the receiving object in the caller, then delete the second temporary (invoking the dtor and the dtor of all member objects, etc...). Even if myClass and of its all member objects have implemented move semantics this may actually be a very heavy duty operation. Better to pass a reference parameter to the receiving object and perhaps use a POD sentinel (success/fail) as the function return type, or use std::unique_ptr as described perfectly by Richard.
So I'm curious of the reason the following code crashes.
Will appreciate help.
#include <iostream>
using namespace std;
class temp
{
public:
temp(int i)
{
intPtr = new int(i);
}
~temp()
{
delete intPtr;
}
private:
int* intPtr;
};
void f (temp fInput)
{
cout << "f called" << endl;
}
int main()
{
temp x = 2;
f(x);
return 0;
}
You are violating The Rule of Three
You maintain a pointer member and you pass a copy of the object to the function f. So, the end result is that you call delete twice on the same pointer.
The crash occurs because of the way you are passing x.
After the scope of the f function, x's destructure will be called and will delete intPtr.
However, that will delete memory that is still in scope for main. Therefor, after return 0 is called, it will try to delete memory that already exists as you are calling delete twice on the same pointer.
To fix this error, change
void f (temp fInput)
to
void f (const temp& fInput)
Alternatively, you could look into using a std::shared_ptr.
The pointer is copied when x is passed (implicit copy constructor) and the destructor is called twice (before the function returns and before main returns), thus the memory is deleted twice.
Use std::shared_ptr<int> here instead instead of a raw int pointer (assuming you want the behavior to be the same, i.e. reference the same int from both temps; otherwise, implement the copy constructor, move constructor and assignment operator yourself).
#include <memory>
class temp {
public:
temp(int i) : intPtr(std::make_shared<int>(i)) {
}
private:
std::shared_ptr<int> intPtr; // reference counting ftw
};
The problem you're hitting here is a double delete. Because you didn't define any copy constructors here C++ is happily doing so for you. The implementation does this by performing a shallow copy of all of the contents.
f(x);
This line works by creating a copy of x and passing it to f. At this point there are 2 temp instances which own a single intPtr member. Both instances will delete that pointer and this is likely what is causing your crash.
To work around this you can take a number of steps
Use a pointer type meant for sharing like shared_ptr<T>
Create an uncallable copy constructor which forces the instance to be passed by ref
An example of #2 is
class temp {
temp(const temp& other);
temp& operator=(const temp& other);
public:
// Same
};
Now the f(x) line simply won't compile because it can't access the necessary copy constructor. It forces it to instead redefine f to prevent a copy.
void f(const temp& fInput) {
...
}
// AirlineTicket.h
class AirlineTicket
{
public:
AirlineTicket();
~AirlineTicket();
int getNumberOfMiles();
private:
int mNumberOfMiles;
};
I want now what is the meaning of ~AirlineTicket(); in this code?
I don't know the meaning of ~ (tilde).
It is the destructor.
It gets called when you destroy (reaching end of scope, or calling delete to a pointer to) the instance of the object.
In the context you're using it, it defines a destructor.
In other context such as the following one, it's also called bitwise negation (complement):
int a = ~100;
int b = ~a;
Output: (ideone)
-101
100
~ signs that it is a destructor and when ever the object goes out of scope, corresponding destructor is called.
When the destructor is called ?
Taking an example -
#include <iostream>
class foo
{
public:
void checkDestructorCall() ;
~foo();
};
void foo::checkDestructorCall()
{
foo objOne; // objOne goes out of scope because of being a locally created object upon return of checkDestructorCall
}
foo:: ~foo()
{
std::cout << "Destructor called \t" << this << "\n";
}
int main()
{
foo obj; // obj goes of scope upon return of main
obj.checkDestructorCall();
return 0;
}
Results on my system:
Destructor called 0xbfec7942
Destructor called 0xbfec7943
This example just serves to indicate when a destructor is called. But destructor is written only when the class manages resources.
When class manages resources?
#include <iostream>
class foo
{
int *ptr;
public:
foo() ; // Constructor
~foo() ;
};
foo:: foo()
{
ptr = new int ; // ptr is managing resources.
// This assignment can be modified to take place in initializer lists too.
}
foo:: ~foo()
{
std::cout << "Destructor called \t" << this << "\n";
delete ptr ; // Returning back the resources acquired from the free store.
// If this isn't done, program gives memory leaks.
}
int main()
{
foo *obj = new foo;
// obj is pointing to resources acquired from free store. Or the object it is pointing to lies on heap. So, we need to explicitly call delete on the pointer object.
delete obj ; // Calls the ~foo
return 0;
}
Results on my system:
Destructor called 0x9b68008
And in the program, you posted I don't see a need to write destructor. Hope it helps !
It's the class destructor. You might want to pick up an introductory text on C++ development, as destructors are a fundamental concept in OOP. There is a good reference site here, and the C++ FAQ is another good resource.
~AirlineTicket(); is the destructor for the class AirlineTicket
see this link for further reference
The destructor is called when you delete the object of that class, to free any memory allocated or resources being used by the object.
~ introduces a destructor. It's used because (a) it was available, ad (b) ~ is one (of several) mathematical notation for "not".
This indicates destructor of class.
http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fcplr380.htm