Introduction To Object Oriented Concepts
Introduction To Object Oriented Concepts
• int main(){
• return 0;
• }
Objects on the heap
• C++ programming language allows both auto(or
stack allocated) and dynamically allocated (heap
allocated) objects.
• C++ supports stack allocated objects for the reason
of runtime efficiency. Stack based objects are
implicitly managed by C++ compiler. They are
destroyed when they go out of scope and
dynamically allocated objects must be manually
released, using delete operator otherwise memory
leak occurs.
Objects on the heap
• #include <iostream>
• using namespace std;
• class Test
• {
• // Notice this, new operator function is private
• void* operator new(size_t size);
• int x;
• public:
• Test() { x = 9; cout << "Constructor is called\n"; }
• void display() { cout << "x = " << x << "\n"; }
• ~Test() { cout << "Destructor is executed\n"; }
• };
•
• int main()
• {
• // following line cause a compile time error.
• Test* obj=new Test(); //Error line
• class Test
• {
• int x;
• public:
• Test (int x){ this->x = x; }
• Test() { x = 9; cout << "Constructor is called\n"; }
• void display() { cout << "x = " << x << "\n"; }
• ~Test() { cout << "Destructor is executed\n"; }
• };
• int main()
• {
• Test t1; t1.display();
• Test t2(100); t2.display();
• return 0;
• }
Overloading of Constant members
• void fun(const int i) { cout << "fun(const int)
called "; } void fun(int i) { cout << "fun(int )
called " ; } int main() { const int i = 10; fun(i);
return 0; }
Mutable Data member
• Mutable data member is that member which
can always be changed; even if the object
is const type.
• To make data member as a mutable, we need
to use mutable keyword.
Mutable Data member
• #include<iostream> • int main()
• using namespace std;
• {
• class Sample • const Sample s(10,20); // A const object
• {
• int x; mutable int y;
• cout<<endl <<"Value before change: ";
• public:
• Sample(int a=0, int b=0) { x=a; y=b;} • s.display();
• //function to set value of x
• void setx(int a=0) {x = a;} • s.setx(100);
• //function to set value of y
• //Error: x can not be changed, because
• //value of y being changed, even if member function is object is constant.
constant. • s.sety(200);
• void sety(int b=0) const {y = b;}
• //y still can be changed, because y is
• //function to display x and y.
mutable.
• //this has to be const type, if member function is
constant type. • cout<<endl<<"Value after change: ";
• void display() const
• {
• s.display();
• cout<<endl<<"x: "<<x<<" y: "<<y<<endl; •
• } • return 0;
• };
• }
Constant objects
• const class objects can only explicitly
call const member functions
• # include <iostream> • int main()
• using namespace std; • {
• const Something something; //
• class Something
calls default constructor
• {
•
•
public:
• int m_value; • //something.m_value = 5; //
• compiler error: violates const
• Something(): m_value(0) { } • //something.setValue(5); //
• compiler error: violates const
• void setValue(int value) { m_value = value; } • cout << something.getValue();
• //int getValue() { return m_value ; }
• return 0;
• int getValue() const { return m_value ; }
• }; • }
Memory Management
• Code Segment: Compiled program with executive instructions are
kept in code segment. It is read only.
• Data Segment: Global variables and static variables are kept in data
segment. It can read / write.
• Stack: A stack is usually pre-allocated memory. The stack is a LIFO
data structure. Each new variable is pushed onto the stack. Once
variable goes out of scope, memory is freed. Once a stack variable is
freed, that region of memory becomes available for other variables.
The stack grows and shrinks as functions push and pop local
variables. It stores local data, return addresses, arguments passed
to functions and current status of memory.
• Heap: Memory is allocated during program execution. Memory is
allocated using dynamic memory allocation and it can be de-
allocated.
New vs malloc
• Memory allocated from 'Free Store' • Memory allocated from 'Heap'
• Returns a fully typed pointer. • Returns a void*
• new (standard version) never returns a
NULL (will throw on failure)
• Returns NULL on failure
• Are called with Type-ID (compiler • Must specify the size required in
calculates the size) bytes.
• Has a version explicitly to handle arrays. • Allocating array requires manual
• Reallocating (to get more space) not calculation of space.
handled intuitively (because of copy
• Reallocating larger chunk of
constructor).
• Whether they call malloc/free is memory simple (No copy
implementation defined. constructor to worry about)
• Can add a new memory allocator to deal • They will NOT call new/delete
with low memory (set_new_handler) • No way to splice user code into
• operator new/delete can be overridden the allocation sequence to help
legally
with low memory.
• constructor/destructor used to
initialize/destroy the object • malloc/free can NOT be
overridden legally
Notes
New vs malloc vs calloc
• malloc allocates uninitialized memory. The allocated
memory has to be released with free. It takes argument as
size of memory to allocate.
• Ex: (typecast datatype pointer) malloc(No_Elements * sizeof(datatype))
• calloc is like malloc but initializes the allocated memory with
0. It needs to be freed with free.
• Ex: (typecast datatype pointer) calloc(No_Elements, sizeof(datatype))
• new initializes the allocated memory by calling the
constructor (if it's an object). Memory allocated
with new should be released with delete (which in turn calls
the destructor). It does not need you to manually specify
the size you need and cast it to the appropriate type.
• Ex: new [Objectconstructor]
Deep copy v/s Shallow copy
• In a deep copy, the fields that • In a shallow copy, a new
are stored by value are copied instance of the type is created
as before, but the pointers to and the values are copied into
objects stored by reference the new instance. The
are not copied. Instead, a reference pointers are also
deep copy is made of the copied just like the values.
referenced object, and a Therefore, the references are
pointer to the new object is pointing to the original objects.
stored. Any changes that are Any changes to the members
made to those referenced that are stored by reference
appear in both the original and
objects will not affect other
the copy, since no copy was
copies of the object
made of the referenced object.
Constructor Overloading
• Constructor can be • class Base{
• public:
overloaded with in a class
• Base(){
similar way as function • cout << "Base Default Constructor";
overloading. • }
• Overloaded constructors • Base(string s){
have the same name (name • cout << "Base Constructor with String
value ";
of the class) but different
• }
number of arguments. • Base(int i){
• Depending upon the • cout << "Base Constructor with integer
number and type of value " << i << endl;
arguments passed, specific • }
• };
constructor is called.
Explicit constructor
• class A { • class A {
• public:
• public: • explicit A();
• A(); • explicit A(int);
•
• A(int); explicit A(string, int = 0); };
• Only following statements are
• A(string, int = 0); legal:
• }; • A a1;
• A a2 = A(1);
• The following • A a3(1);
declarations are legal. • A a4 = A("Venditti");
• A c = 1; • A* p = new A(1);
• A a5 = (A)1;
• A d = (string)"Venditti"; • A a6 = static_cast<A>(1);
Copy constructor & Generic Copy constructor
Copy Constructor v/s Assignment operator
• De-allocating memory
• delete free the allocated memory and calls destructor.
But free() de-allocate memory but does not call
destructor. delete is faster than free() because an
operator is always faster than a function.
• Explicit call to destructor is only necessary when object
is placed at particular location in memory by using
placement new. Destructor should not be called
explicitly when the object is dynamically allocated
because delete operator automatically calls destructor
•
• int main()
class Base{
• public: • {
• Base(){ • Test(); // Explicit call to
• cout << "Bsae Constructor";
constructor
• }
• ~Base(){ • cout << endl;
• cout << "Bsae Distructor"; • Test t; // local object
• }
• cout << endl;
• };
• class Test : Base • t.~Test(); // Explicit call to
• { destructor
• public: • cout << endl;
• Test() { cout << "Constructor is
executed\n"; } • t.simple();
• ~Test() { cout << "Destructor is • cout << endl;
executed\n"; }
• void simple(){ • return 0;
• cout << "Simple method"; • }
• }
• }; Notes
• We know that the destructor is a routine that has
the same name as the class with a ~ symbol
preceding it and is called automatically by the
compiler during the destruction of an object. A
compiler synthesized destructor for class would do
a member wise destruction of the member objects
of class objects and their base class sub-objects. i.e.
if user-defined destructors are available for any
member objects or base class sub-objects, the
compiler would invoke the corresponding methods.
Preventing destroying object instance
• int main () {
• try
• {
• throw myex;
• }
• catch (exception& e)
• {
• cout << e.what() << '\n';
• }
• return 0;
• }
exception description
bad_alloc thrown by new on allocation failure
thrown by dynamic_cast when it fails
bad_cast
in a dynamic cast
thrown by certain dynamic exception
bad_exception
specifiers
bad_typeid thrown by typeid
bad_function_call thrown by empty function objects
thrown by shared_ptr when passed
bad_weak_ptr
a bad weak_ptr
Header <exception> defines two generic exception types that can
be inherited by custom exceptions to report errors
exception description
error related to the internal logic of
logic_error
the program
try
{
double res = divide ( 1, 0);
}
catch ( char* c)
{
cout << c;
}
Custom Exceptions
• int main()
• class MyException : {
public std::exception try
{
{ throw MyException();
}
const char * what () catch (MyException& e)
const throw () {
std::cout << "MyException caught" <<
{ std::endl;
return "C++ Exception"; std::cout << e.what() << std::endl;
}
} catch (std::exception& e)
{
} // Other errors
}
}
Custom Exceptions
• class MyException : public std::exception {
const char* file;
int line; • int main()
const char* func; {
const char* info; try
{
public:
MyException(const char* msg, const
some_function()
char* file_, int line_, const char* func_, }
const char* info_ = "") : catch (MyException& ex)
std::exception(msg), {
file (file_), std::cout << ex.what() << ex.get_info()
line (line_), << std::endl;
func (func_), std::cout << "Function: " <<
info (info_){ }
ex.get_func() << std::endl;
const char* get_file() const { return file; }
int get_line() const { return line; } return EXIT_FAILURE;
const char* get_func() const { return func; }
}
const char* get_info() const { return }
info; }