Constructors, Destructors & memory Management

Download Report

Transcript Constructors, Destructors & memory Management

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(); // Constructor for class X }; ~X(); // Destructor for class X

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 following are true: is trivial if all the  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 generated A or a member of A has a destructor, and a class derived from A does not declare a destructor, a default destructor is

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.

2.

3.

The destructor for a class object is called before destructors for members and bases are called Destructors for non-static members are called before destructors for base classes are called.

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 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 #include 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 free store but in the memory allocated by p not in the 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 in Java and similar languages finally construct 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 // for std::auto_ptr #include class Simple { public: std::auto_ptr 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(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: int* ptr;

What do these 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 “pointer like”  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