Object-Oriented Programming Using C++

processroguishΛογισμικό & κατασκευή λογ/κού

18 Νοε 2013 (πριν από 4 χρόνια και 1 μήνα)

81 εμφανίσεις

Constructors and
Destructors



Outline


Constructors and Destructors


Default and compiler
-
generated


Overloading


Member initialization vs. assignment


Initializing const, reference and static members


Copy constructor


Assignment operator


Constructors as type conversion operators


Destructors

Declaring and initializing
simple variables


When variables of native types are
declared there is no question about how
much memory to allocate



Initialization is the process of storing a
value in newly declared variables

Initializing simple
variables

int counter = 0;

char* owner = NULL;

char name[] = “C++”;

double coef[3] = { 0, 1.2, 3.0 };


Initializing class objects


Classes are complex (derived) types


They may have many components


Example: Employee class has


name, address, id, etc.


Declaration of a variable that is a class
(instantiating an object) requires the
‘construction’ of an object by setting aside the
required blocks of memory.

Initializing objects


Constructors are special class methods
that create an object of that class


they have the same name as their class


Example:


Class Employee


{ public:



Employee() {}; // default constructor

What constructors do


Have same name as the class


Are called automatically when class object
declared


Initialize (non
-
static) members


Employee() { id = 0; }


Allocate memory for dynamic members


Employee() { char* nameptr = new char[20];}


Allocate any needed resources

Date class in C++

class Date {

public: // services

Date();

unsigned getMonth();

void incrDay();

unsigned getDOW();

// more services...

private: // state

unsigned year, day, month;

}

Constructors for the Date
class

default
-

all decisions are made for you


Date today;

initializing
-

you set basic parameters


Date today(9,20,1999);

copy
-

creates an object that is a copy of
another object


Date monday(today);

Constructors

for the Date class

class Date {

public:

// default constructor

Date();

// explicitly specifying m,d,y

Date(unsigned m, unsigned d,


unsigned y);

// copy constructor

Date(const Date& _date);

// parsing a string MM/DD/YYYY

Date(const string& DateStr);


When do constructors get
called?


Block local (automatic) object


Dynamically allocated object


Class argument passed by value


Class object returned by value


Class member that is an object


Array element


Temporary object


Local static object


Global object

Local object

{


Date today; // constructor





// implicitly called





} // destructor implicitly


// called here



Dynamic object

{


pstr = new string(“5113”);


...


delete pstr; // destructor called


...

}


Default constructor


Takes no arguments, or


All arguments have default values


Date::Date() {…}


Date::Date(unsigned m = 0,


unsigned d = 0,


unsigned y = 0) { … }


Date::Date() constructor

Date::Date () {


time_t clock = time(0);


struct tm* tmptr =


localtime(&clock);




month = tm_month;


day = tmptr
-
>tm_day;


year = 1900 + tmptr
-
>tm_year;

}

Default constructor (cont.)


Called when object declared without any
arguments


Date d;


string s;

Compiler
-
generated
constructor


What if we do not supply any constructor for
a class?


Compiler generates one with an empty body


Date() {};



No data members initialized


Do not rely on compiler
-
generated
constructors

Disabling a default
constructor


Default constructors do not make sense
for some classes


classes with no reasonable defaults


class BankAccount { ... };


BankAccount ba; // nameless account?


Disabling a default
constructor (cont).


Solution: make a default constructor
private


BankAccount {


...


private:


BankAccount();

}


Prevents compiler from supplying one

BankAccount ba; // will not compile

Constructing arrays of
objects


A default constructor is needed


user
-

or compiler
-
supplied


Complex cmplarr[10];

Date datearr[20];



There is no way to call constructors with
arguments (non
-
default) for array
members

Arrays of objects and non
-
default constructors


Trick: declare a
pointer to an array

of
objects


Allocate and initialize each object in a loop


Date* dates[31];


for (int day = 0; day < 31; ++day) {


dates[i] = new Date(9, day, 1999);

}

Multiple constructors


What if you wanted your program to be
able to create Date objects in a variety of
formats?


Date today(9,20,1999);


Date today(“Sept.,20, 1999”);


Date today(9,20,1999), same_as_today;
same_as_today(today);


To do this we must have different
versions of the Date constructor.

Overloading constructors


Multiple ways to initialize a Date objects


from Month, Day, Year


from a date string in a known format


from another Date object





Overloaded constructors


different signatures (types and numbers of
arguments)

Calling overloaded
constructors

Date today; // default constructor


// explicitly specify m, d, y

Date fdc(12, 31, 2000);


// initialize date from string

Date eoq(“12/12/1998”);


// from another date object

Date eoq2(eoq);

Common tasks of
overloaded constructors


Checking arguments


Incrementing the number of instances


All Date constructors could call a private
class method to verify input and assign
values to private data members


Date::assign(month, day, year)


Date::assign method

Date::Date () {


// initialize struct tm* tmptr...



// assign checks its arguments


assign(tmptr
-
>tm_month,


tmptr
-
>tm_mday,


1900 + tmptr
-
>tm_year);

}


Date::assign method

Date::Date (constr string DateStr) {


// parse m, d, y from DateStr...



// assign checks its arguments


assign(m, d, y);

}


Date::assign method

void Date::assign(unsigned _month,

unsigned _day, unsigned _year)


{


setMonth(_month);


setDay(_day);


setYear(_year);



// counter of Dates instantiated


numCreated++;

}

Direct assignment from the
constructor


Rather than calling a private ‘assign’
function, the constructor could do the
assignment itself.


Advantage of using ‘assign’ is the isolation
of the verification process to that function.

Assignment vs. initialization
in constructors


Assignment


in the constructor body

Date::Date(unsigned m,


unsigned d,


unsigned y)




{


month = m;


day = d;


year = y;


}

Assignment vs. initialization
in constructors


Initialization


in the initialization list


Date::Date(unsigned m,


unsigned d,


unsigned y)




:




month(m), day(d), year(y)

{};

Empty function body

Initialization list

Assignment vs. initialization
in constructors

class Student {


private:


string name;


public:


Student(string aName)


{ name = aName; };


...


Why initialization is preferred
to assignment


When a Student object is constructed,


a default constructor for
string name

is called


Student(string aName)


{ string name = aName };



this allocates (1 byte of) memory


inside
Student::Student

body, string
assignment operator is called


memory is freed and allocated again

A better way of initialization


Uses initialization


Avoids allocation, deallocation,
reallocation for name


Student(string aName) :


name(aName) {};


Problem: Order of initialization


Initialization takes place in the order that
variables were declared


Not the order in which members are in
the initialization list!


Subtle dependencies possible which could
cause problems (next slide)


Pitfalls of initialization lists

class my_string {


private:


char* rep;


unsigned len;


public:


my_string(const char* cp) :


len(strlen(cp)),


rep(new char[len+1]) {};


Member
len

initialized after
rep


But
rep

uses
len
...

Initializing const and ref
members


Assignment to constants and references
prohibited


Initialization required



Const and reference members must be
initialized in the constructor initialization
lists

Initializing static and
global objects


Few reasons to have global objects


Initialized in the order of declaration


Dependencies between global objects in
different files


If you must, make them local static


Initialized when control thread first passes
over

Copy constructor


Initializes a new object from another,
existing one


Signature:
Class::Class(const
Class&);


Copy constructor for class
Date

Date::Date(const Date& date) {


// no need to check passed date arg


month = date.month;


day = date.day;


year = date.year;

}


Uses of the copy
constructor


Implicitly called in 3 situations


defining a new object from an existing object


passing an object by value


returning an object by value

Copy constructor:

defining a new object

Date eosem(“12/20/1999”);


// init 2 local objects from eosem

Date eosem2(eosem);// pass by value

Date eosem3 = eosem;// return value


// init a dynamic object from eosem

Date pdate = new Date(eosem);

Copy constructor: passing
objects by value

//copy ctor called for each value arg

unsigned dateDiff(Date d1, Date d2);

...

Date today;

Date eosem(12, 20, 1999);

cout << dateDiff(eosem, today);


Avoiding copy constructors


To avoid unnecessarily calling copy
constructors


Do not pass object by value; pass by
const ref


unsigned dateDiff(const Date& d1,


const Date& d2);



Compiler
-
supplied copy
constructor


Copies members bitwise


OK for built
-
in types


Problematic for
resources owned by
object


Slack bytes problem

Compiler
-
supplied copy
constructor: problems

class Date {


char* charRep; // owned by Date


...


Date today;

Date today2(today);

m:9 d:20 y:1999

charRep*

today2

m:9 d:20 y:1999

charRep*

today

9/20/1999

Shallow copy by default


The problem with the compiler supplied
copy constructor is that it is a shallow
copy constructor.


In the last example, both today and
today2 consist of pointers set to the same
block of memory.


Thus, a change to one date will affect the
other.

Constructors as type
conversion operators


Constructors take a single argument of a
given type


by value or by const reference



They implicitly convert it into the passed
type (see example on next 2 slides)

Constructors as type
conversion operators

class my_string {


public:


my_string(unsigned slen) :


rep = new char[slen+1],


len(slen)


{ memset(rep, ‘ ‘, len); };


private:


char* rep;


unsigned len;

Constructors as type
conversion operators

my_string s = ‘a’;



char ‘a’ is promoted to unsigned 97


a string of 97 spaces is created


probably not the desired result


Keyword
explicit

does not allow
implicit conversions

explicit

my_string(unsigned slen);

Destructors


Called automatically when local objects go
out of scope


Called implicitly when dynamic objects are
delete
d

Assignment operator


Similar to the copy constructor, but


Re
-
initializes already constructed objects


Date today; // copy constructor

...

today = “9/20/1999”;



Need assignment operator accepting
char*

Assignment operator

Class Date {


Date& operator=(const char* dCptr);


...

}


Date::operator=(const char* dCptr) {


// parse dateCptr into m, d, y


assign(m, d, y);

}

Assignment operator


New C++ feature: overloading operators


Operator overloading
-

next lecture


Compiler generates a default assignment
operator if you do not define one


bitwise copy only

Assignment operator


Bitwise copy ok for classes like Date


members of simple types only


no pointers => no remote ownership



What happens if we bitwise copy an
object owning a resource?


Same problem as with default copy
constructors

Rule of Big Three


If a class has any of


copy constructor


destructor


assignment operator



It needs all three


Destructor usually shows up first


Exception: when destructor only
decrements number of instances