Constructors, Destructors & memory Management

advertisement
Constructors,
Destructors &
Memory Management
Computer Science I
Constructors: A Review!
 A member function with the same name as its
class
class X {
public:
X( );
for class X
};
// constructor
 Used to create, and can initialize, objects of
their class type
Constructors: A Review!
 You cannot declare a constructor as virtual or
static, nor can you declare a constructor as const,
volatile, or const volatile
 You do not specify a return type for a
constructor
 A return statement in the body of a constructor
cannot have a return value
Destructors
 Used to deallocate memory and do other
cleanup for a class object and its class members
when the object is destroyed
 Called for a class object when that object passes
out of scope or is explicitly deleted
Destructors
A member function with the same name as its
class prefixed by a ~ (tilde)
For example:
class X{
public:
X();
class X
~X();
class X
};
// Constructor for
// Destructor for
Destructors
 Takes no arguments and has no return type
 Its address cannot be taken
 Cannot be declared const, volatile, const volatile
or static
 Can be declared virtual or pure virtual
Implicitly Declared Destructors
 If no user-defined destructor exists for a class
and one is needed, the compiler implicitly
declares a destructor
 This implicitly declared destructor is an inline
public member of its class
Implicitly Declared Destructors
 The compiler will implicitly define an implicitly
declared destructor when the compiler uses the
destructor to destroy an object of the
destructor's class type
Implicitly Declared Destructors
 Suppose a class A has an implicitly declared destructor
 The following is equivalent to the function the
compiler would implicitly define for A:
A::~A( ) { }
 The compiler first implicitly defines the implicitly
declared destructors of the base classes and non-static
data members of a class A before defining the implicitly
declared destructor of A
Trivial vs. Nontrivial
 A destructor of a class A is trivial if all the
following are true:
 It is implicitly defined
 All the direct base classes of A have trivial
destructors
 The classes of all the non-static data members of A
have trivial destructors
 If any of the above are false, then the destructor
is nontrivial.
Derived & Member Classes
 Class members that are class types can have
their own destructors
 Both base and derived classes can have
destructors, although destructors are not
inherited
 If a base class A or a member of A has a
destructor, and a class derived from A does not
declare a destructor, a default destructor is
generated
Order of Execution
 The default destructor calls the destructors of the
base class and members of the derived class
 The destructors of base classes and members are
called in the reverse order of the completion of their
constructor
1. The destructor for a class object is called before destructors
for members and bases are called
2. Destructors for non-static members are called before
destructors for base classes are called.
3. Destructors for non-virtual base classes are called before
destructors for virtual base classes are called
Exceptions
 When an exception is thrown for a class object
with a destructor, the destructor for the
temporary object thrown is not called until
control passes out of the catch block
Destructors
 Implicitly called when an automatic object (a
local object that has been declared auto or
register, or not declared as static or extern) or
temporary object passes out of scope
 Implicitly called at program termination for
constructed external and static objects
 Invoked when you use the delete operator for
objects created with the new operator
Example
#include <string>
class Y {
private:
char * string;
int number;
public:
// Constructor
Y(const char*, int);
// Destructor
~Y() { delete[] string; } };
// Define class Y constructor
Y::Y(const char* n, int a) {
string = strcpy(new
char[strlen(n) + 1 ], n);
number = a;
}
int main () {
// Create and initialize
// object of class Y
Y yobj = Y("somestring", 10);
// ...
// Destructor ~Y is called before
// control returns from main()
}
Destructors
 You can use a destructor explicitly to destroy
objects, although this practice is not
recommended
 However to destroy an object created with the
placement new operator, you can explicitly call
the object's destructor
Example
#include <new>
#include <iostream>
using namespace std;
class A {
public:
A( ) {
cout << "A::A( )" << endl;
}
~A( ) {
cout << "A::~A( )" << endl;
}
};
int main ( ) {
char* p = new char[sizeof(A)];
A* ap = new (p) A;
ap->A::~A( );
delete [ ] p;
}
Destructors
 The statement
A* ap = new (p) A
dynamically creates a new object of type A not in the
free store but in the memory allocated by p
 The statement
delete [ ] p
will delete the storage allocated by p
 But the run time will still believe that the object
pointed to by ap still exists until you explicitly call the
destructor of A
(with the statement ap->A::~A( ) )
Garbage Collection and RAII
 Garbage Collection (GC) deals with the
management of dynamic memory, with different
levels of automation
 The construct, collector, attempts to reclaim
garbage
 Garbage is memory that was used by application
objects that will never be accessed or mutated
again
Garbage Collection and RAII
 This is often regarded as an important feature of
recent languages, especially if they forbid
manual memory management, that is very prone
to errors and therefore requires an high level of
experience from programmers
 Errors due to memory management result
mostly in instabilities and crashes that are only
noticed at runtime, making them extremely hard
to detect and correct
Garbage Collection & C++
 C++ has optional support for garbage
collection and some implementations include
garbage collection do exist
 The C++ standard defines the implementation
of the language and it's underlining platform
opening it for the inclusion of extensions
 For instance, Sun's C++ compiler product does
include the libgc library, a conservative garbage
collector
Garbage Collection & C++
 Unlike many high level languages, C++ does
not impose the use of garbage collection
 Mainstream C++ idioms for memory
management do not assume the use of
conventional automated garbage collection
Resource Acquisition Is
Initialization
 The most common garbage collection method
in C++ is the use of the strangely named idiom
"RAII (Resource Acquisition Is Initialization)
 The key idea is that a resource, whether
acquired at initialization time or not, is owned
by an object, and …
 That the object's destructor will automate the
release of that resource at an appropriate time
Resource Acquisition Is
Initialization
 This enables C++, through RAII, to support
deterministic cleanup of resources, since the
same approaches that work for freeing memory
can also be used to release other resources
(i.e., file handles, mutexes, database
connections, transactions, etc.)
Resource Acquisition Is
Initialization
 In the absence of a default garbage collection,
RAII is a robust way to ensure that resources
are not leaked even in code that might cause
exceptions to be thrown
 It is arguably superior to the finally construct
in Java and similar languages
 In C++ the class provides a destructor, and
users of that class don't need to do anything
except ensure that the object is destroyed when
they are finished with it
Smart Pointers for Memory
Management
 Smart pointer type is any class type that
overloads operator->, operator*, or operator>*
 They are not really pointers at all, but
overloading these operators allows a smart
pointer to behave much like a built-in pointer,
and much code can be written which works
with both "real" pointers and smart pointers
std::auto_ptr
 The only smart pointer type included in the
2003 C++ Standard is std::auto_ptr
 While this has certain uses, it is not the most
elegant or capable of smart pointer designs
 Provides the ability to:
 simulate the lifetime of a local variable or member
variable for an object that is actually dynamically
allocated
 provide a mechanism for "transfer of ownership" of
objects from one owner to another
Example
#include <memory> // for std::auto_ptr
#include <iostream>
class Simple {
public:
std::auto_ptr<int> theInt;
Simple() : theInt(new int( )) {
*theInt = 3;
//get object like normal pointer
}
int f( ) {
return 42;
}
// when this class is destroyed, theInt will
// automatically be freed
};
int main( ) {
std::auto_ptr<Simple> simple(new Simple( ));
// access member functions like normal pointers
std::cout << simple->f( );
// the Simple object is freed when simple goes out
of scope
return 0;
Creating Your Own Smart
Pointer Type
 One of the rationale of using smart pointers is
to avoid leaking memory
 In order to avoid this, we should avoid manually
managing heap-base memory
 We have to find a container which can
automatically return the memory back to the
operation system when we do not use it
Creating Your Own Smart
Pointer Type
 The destructor of class can match this
requirement.
 What we need to store in a basic smart pointer
is, of course, the address of the allocated
memory
 For this, we can simply use a pointer
Creating Your Own Smart
Pointer Type
 Assume we are executing a design for storing a
piece of memory for an int
class smt_ptr
{
private:
int* ptr;
};
Creating Your Own Smart
Pointer Type
 In order to make sure that every user puts an address
in this smart pointer when doing initialization, we have
to specify the constructor to accept a declaration of this
smart pointer with the target address as the argument,
but not "mere declaration" of the smart pointer itself
class smt_ptr
{
public:
explicit smt_ptr(int* pointer) : ptr(pointer)
{ }
private:
What do these
int* ptr;
mean?
};
Creating Your Own Smart
Pointer Type
 Now, we have to specify the class to "delete"
the pointer when the instance of this smart
pointer destructs
class smt_ptr
{
public:
explicit smt_ptr(int* pointer) : ptr(pointer)
{ }
~smt_ptr() { delete ptr; }
private:
int* ptr;
};
Creating Your Own Smart
Pointer Type
 We have to allow users to access the data stored
in this smart pointer and make it more “pointerlike”
 For this, we may add a function to provide the
access the raw pointer, and overload some
operators,
(such as operator* and operator->)
to make it behave like a real pointer
Creating Your Own Smart
Pointer Type
class smt_ptr
{
public:
explicit smt_ptr(int* pointer) : ptr(pointer)
{ }
// Declares these functions const to indicate that
// there is no modification to the data members.
~smt_ptr() { delete ptr; }
int* get() const { return ptr; }
int* operator->() const { return ptr; }
int& operator*() const { return *ptr; }
private:
int* ptr;
};
Final Thought
 However, to make this "homemade" smart
pointer work with other data types and classes,
we have to turn it into a class template
 But Templates is a topic for CS171 
Download