C++ object created with new, destroyed with free(); How bad is this?
Yes it does matter.
For memory obtained using new
you must use delete
.
For memory obtained using malloc
you must use free
.
new
and malloc
may use different data structures internally to keep track of what and where it has allocated memory. So in order to free memory, you have to call that corresponding function that knows about those data structures. It is however generally a bad idea to mix these two types of memory allocation in a piece of code.
c++ malloc and free object creation and deletion
Reading so many points from so many I myself wrote a program to explain your problem. See below :-
#include <iostream>
#include <vector>
using namespace std;
class X
{
int x;
vector<int> v;
public:
X()
{
cout<<"constructing\n";
x=0;
v.push_back(1);
v.push_back(2);
}
int& getx()
{
return x;
}
vector<int>& getv()
{
return v;
}
~X()
{
cout<<"destroying\n";
}
};
int main()
{
X* p=(X*)operator new(sizeof(X));
++p->getx();
p->getx()*=5;
cout<<p->getx()<<"\n";
for (int x:p->getv())
cout<<x<<" ";
cout<<"\nexecuted\n";
operator delete(p);
return 0;
}
/* Output :-
5
executed
*/
See how p
ignored the vector v
and went for the line executed
. This is because vector<int>
being a class
(or more precisely a class template
) was never created by operator new (or malloc in your case)
. Your program showed the output for x
because it is a primitive type & not a class. For a class you need a constructor & hence operator new or malloc
aren't suitable for classes & hence the output. If you simply replace operator new with new
& operator delete with delete
then the output will be :-
constructing
5
1 2
executed
destroying
Now your code gives the correct & apt results ! Congo !
For your second question, NEVER MIX UP malloc & free
with new & delete
as it creates UB with not so happening results.
Object destruction in C++
In the following text, I will distinguish between scoped objects, whose time of destruction is statically determined by their enclosing scope (functions, blocks, classes, expressions), and dynamic objects, whose exact time of destruction is generally not known until runtime.
While the destruction semantics of class objects are determined by destructors, the destruction of a scalar object is always a no-op. Specifically, destructing a pointer variable does not destroy the pointee.
Scoped objects
automatic objects
Automatic objects (commonly referred to as "local variables") are destructed, in reverse order of their definition, when control flow leaves the scope of their definition:
void some_function()
{
Foo a;
Foo b;
if (some_condition)
{
Foo y;
Foo z;
} <--- z and y are destructed here
} <--- b and a are destructed here
If an exception is thrown during the execution of a function, all previously constructed automatic objects are destructed before the exception is propagated to the caller. This process is called stack unwinding. During stack unwinding, no further exceptions may leave the destructors of the aforementioned previously constructed automatic objects. Otherwise, the function std::terminate
is called.
This leads to one of the most important guidelines in C++:
Destructors should never throw.
non-local static objects
Static objects defined at namespace scope (commonly referred to as "global variables") and static data members are destructed, in reverse order of their definition, after the execution of main
:
struct X
{
static Foo x; // this is only a *declaration*, not a *definition*
};
Foo a;
Foo b;
int main()
{
} <--- y, x, b and a are destructed here
Foo X::x; // this is the respective definition
Foo y;
Note that the relative order of construction (and destruction) of static objects defined in different translation units is undefined.
If an exception leaves the destructor of a static object, the function std::terminate
is called.
local static objects
Static objects defined inside functions are constructed when (and if) control flow passes through their definition for the first time.1
They are destructed in reverse order after the execution of main
:
Foo& get_some_Foo()
{
static Foo x;
return x;
}
Bar& get_some_Bar()
{
static Bar y;
return y;
}
int main()
{
get_some_Bar().do_something(); // note that get_some_Bar is called *first*
get_some_Foo().do_something();
} <--- x and y are destructed here // hence y is destructed *last*
If an exception leaves the destructor of a static object, the function std::terminate
is called.
1: This is an extremely simplified model. The initialization details of static objects are actually much more complicated.
base class subobjects and member subobjects
When control flow leaves the destructor body of an object, its member subobjects (also known as its "data members") are destructed in reverse order of their definition. After that, its base class subobjects are destructed in reverse order of the base-specifier-list:
class Foo : Bar, Baz
{
Quux x;
Quux y;
public:
~Foo()
{
} <--- y and x are destructed here,
}; followed by the Baz and Bar base class subobjects
If an exception is thrown during the construction of one of Foo
's subobjects, then all its previously constructed subobjects will be destructed before the exception is propagated. The Foo
destructor, on the other hand, will not be executed, since the Foo
object was never fully constructed.
Note that the destructor body is not responsible for destructing the data members themselves. You only need to write a destructor if a data member is a handle to a resource that needs to be released when the object is destructed (such as a file, a socket, a database connection, a mutex, or heap memory).
array elements
Array elements are destructed in descending order. If an exception is thrown during the construction of the n-th element, the elements n-1 to 0 are destructed before the exception is propagated.
temporary objects
A temporary object is constructed when a prvalue expression of class type is evaluated. The most prominent example of a prvalue expression is the call of a function that returns an object by value, such as T operator+(const T&, const T&)
. Under normal circumstances, the temporary object is destructed when the full-expression that lexically contains the prvalue is completely evaluated:
__________________________ full-expression
___________ subexpression
_______ subexpression
some_function(a + " " + b);
^ both temporary objects are destructed here
The above function call some_function(a + " " + b)
is a full-expression because it is not part of a larger expression (instead, it is part of an expression-statement). Hence, all temporary objects that are constructed during the evaluation of the subexpressions will be destructed at the semicolon. There are two such temporary objects: the first is constructed during the first addition, and the second is constructed during the second addition. The second temporary object will be destructed before the first.
If an exception is thrown during the second addition, the first temporary object will be destructed properly before propagating the exception.
If a local reference is initialized with a prvalue expression, the lifetime of the temporary object is extended to the scope of the local reference, so you won't get a dangling reference:
{
const Foo& r = a + " " + b;
^ first temporary (a + " ") is destructed here
// ...
} <--- second temporary (a + " " + b) is destructed not until here
If a prvalue expression of non-class type is evaluated, the result is a value, not a temporary object. However, a temporary object will be constructed if the prvalue is used to initialize a reference:
const int& r = i + j;
Dynamic objects and arrays
In the following section, destroy X means "first destruct X and then release the underlying memory".
Similarly, create X means "first allocate enough memory and then construct X there".
dynamic objects
A dynamic object created via p = new Foo
is destroyed via delete p
. If you forget to delete p
, you have a resource leak. You should never attempt to do one of the following, since they all lead to undefined behavior:
- destroy a dynamic object via
delete[]
(note the square brackets),free
or any other means - destroy a dynamic object multiple times
- access a dynamic object after it has been destroyed
If an exception is thrown during the construction of a dynamic object, the underlying memory is released before the exception is propagated.
(The destructor will not be executed prior to memory release, because the object was never fully constructed.)
dynamic arrays
A dynamic array created via p = new Foo[n]
is destroyed via delete[] p
(note the square brackets). If you forget to delete[] p
, you have a resource leak. You should never attempt to do one of the following, since they all lead to undefined behavior:
- destroy a dynamic array via
delete
,free
or any other means - destroy a dynamic array multiple times
- access a dynamic array after it has been destroyed
If an exception is thrown during the construction of the n-th element, the elements n-1 to 0 are destructed in descending order, the underlying memory is released, and the exception is propagated.
(You should generally prefer std::vector<Foo>
over Foo*
for dynamic arrays. It makes writing correct and robust code much easier.)
reference-counting smart pointers
A dynamic object managed by several std::shared_ptr<Foo>
objects is destroyed during the destruction of the last std::shared_ptr<Foo>
object involved in sharing that dynamic object.
(You should generally prefer std::shared_ptr<Foo>
over Foo*
for shared objects. It makes writing correct and robust code much easier.)
Why does the use of 'new' cause memory leaks?
What is happening
When you write T t;
you're creating an object of type T
with automatic storage duration. It will get cleaned up automatically when it goes out of scope.
When you write new T()
you're creating an object of type T
with dynamic storage duration. It won't get cleaned up automatically.
You need to pass a pointer to it to delete
in order to clean it up:
However, your second example is worse: you're dereferencing the pointer, and making a copy of the object. This way you lose the pointer to the object created with new
, so you can never delete it even if you wanted!
What you should do
You should prefer automatic storage duration. Need a new object, just write:
A a; // a new object of type A
B b; // a new object of type B
If you do need dynamic storage duration, store the pointer to the allocated object in an automatic storage duration object that deletes it automatically.
template <typename T>
class automatic_pointer {
public:
automatic_pointer(T* pointer) : pointer(pointer) {}
// destructor: gets called upon cleanup
// in this case, we want to use delete
~automatic_pointer() { delete pointer; }
// emulate pointers!
// with this we can write *p
T& operator*() const { return *pointer; }
// and with this we can write p->f()
T* operator->() const { return pointer; }
private:
T* pointer;
// for this example, I'll just forbid copies
// a smarter class could deal with this some other way
automatic_pointer(automatic_pointer const&);
automatic_pointer& operator=(automatic_pointer const&);
};
automatic_pointer<A> a(new A()); // acts like a pointer, but deletes automatically
automatic_pointer<B> b(new B()); // acts like a pointer, but deletes automatically
This is a common idiom that goes by the not-very-descriptive name RAII (Resource Acquisition Is Initialization). When you acquire a resource that needs cleanup, you stick it in an object of automatic storage duration so you don't need to worry about cleaning it up. This applies to any resource, be it memory, open files, network connections, or whatever you fancy.
This automatic_pointer
thing already exists in various forms, I've just provided it to give an example. A very similar class exists in the standard library called std::unique_ptr
.
There's also an old one (pre-C++11) named auto_ptr
but it's now deprecated because it has a strange copying behaviour.
And then there are some even smarter examples, like std::shared_ptr
, that allows multiple pointers to the same object and only cleans it up when the last pointer is destroyed.
Revive object from destructor in C++?
The short answer is: no. C++ does not employ garbage collection, like Java or C#. When an object is destroyed, it's destroyed immediately. Gone for good. Joined the choir invisible. Pining for the fjords, etc...
And to say this over a couple of times in different words so that there is no possible weasily reinterpretation...
The destructor is invoked as part of object destruction. Object destruction consists of invoking the destructor and deallocating the memory that was used for the object itself. It's a single process, not two separate processes. While the destructor is running, the object still exists, for the destructor to use, but it exists on borrowed time. It's a foregone conclusion that the object is going to be vaporized as soon as the destructor returns. Once a destructor is invoked, the object is going to be destroyed, and nothing is going to change its fate.
Understand this: the reason why a destructor is being invoked is because either: the object was originally allocated on the heap with "new", and it's now being "delete"d. "delete" means "delete", not "delete maybe". So the object's getting deleted. Or, if the object was allocated on the stack, the execution thread exited the scope, so all objects declared in the scope are getting destroyed. The destructor is, technically, getting invoked as a result of the object being destroyed. So, the object is being destroyed. The End.
Having said that, C++ allows you to implement a custom allocator for your classes. If you feel like it, you can write your own custom memory allocation and deallocation functions that implement whatever functionality you want. Though these are never used for stack allocated objects (i.e. local variables).
What is the use of creating objects on the free store?
In your example, there is none, and it isn't good practice to
use dynamic allocation. Dynamic allocation is used when objects
have identity (or cannot be copied for some other reason), and
the lifetime of the object does not correspond to some
pre-determined lifetime, like static or auto. Dynamic
allocation may also be used in a few cases where copying is
expensive, and the profiler shows that the copying is
a bottleneck; in such cases, using dynamic allocation and
copying a pointer may remove the bottleneck. (But this should
never be done until profiling shows it necessary.)
Do I need to destroy each instance of new class?
Destructors are called automatically and you usually don't have to worry about it unless you're allocating memory dynamically with new
.
In that case, you will have to call delete
for each allocated memory once you don't need it any more.
Note that if you're allocating an array with new []
, you will have to use delete[]
for desallocation:
int *a = new int[10]; // allocating an array of 10 integers
//...
delete[] a; // release memory
In modern C++, you should consider managed pointers which will do the job for you. Something like:
while(read >> f >> l >> t >> s >> sta >> acct >> bal)
{
cout << "test" << endl;
ptr[i] = std::make_unique<account>(f,l,t,s,sta,acct,bal);
ptr[i]->printcontents();
cout << "test" << endl;
i++;
cout << i << endl;
}
Here, std::make_unique
will return a std::unique_ptr
which will call delete on the associated memory when destroyed.
Last point : are you sure you really need pointers? Hard to say from your example, but depending on your usage you may as well create statically allocated objects:
while(read >> f >> l >> t >> s >> sta >> acct >> bal)
{
cout << "test" << endl;
ptr[i] = account(f,l,t,s,sta,acct,bal);
ptr[i].printcontents();
cout << "test" << endl;
i++;
cout << i << endl;
}
Related Topics
C/C++: Static Function in Header File, What Does It Mean
Use of Typename Keyword with Template Function Parameters
Why Is (Void) 0 a No Operation in C and C++
Dead Code Identification (C++)
What C++ Library Should I Use to Implement a Http Client
C++ Stl: Array VS Vector: Raw Element Accessing Performance
How to Get the Type of a Lambda Argument
Incompatible with Parameter of Type "Lpcwstr"
Visual Studio: Link:Fatal Error Lnk1181: Cannot Open Input File
What Does _T Stands for in a Cstring
Is It True That There Is No Need to Learn C Because C++ Contains Everything
What Can Make C++ Rtti Undesirable to Use
What Does the "L" Mean at the End of an Integer Literal
How to Erase & Delete Pointers to Objects Stored in a Vector
C++ Linking Error After Upgrading to MAC Os X 10.9/Xcode 5.0.1