advanced issues on classes

24
1 Advanced Issues on Advanced Issues on Classes Classes Part 3 Reference variables (Tapestry pp.581, Horton 176 – 178) Const-reference variables (Horton 176 – 178) object sharing: sharing an object/variable among several objects using reference variables and pointers (Tapestry 578 – 583) Static data members in classes (Tapestry 484 – 486, Horton 322 – 325)

Upload: katelyn-malone

Post on 30-Dec-2015

31 views

Category:

Documents


1 download

DESCRIPTION

Advanced Issues on Classes. Part 3 Reference variables (Tapestry pp.581, Horton 176 – 178) Const-reference variables (Horton 176 – 178) object sharing: sharing an object/variable among several objects using reference variables and pointers (Tapestry 578 – 583) - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Advanced Issues on Classes

1

Advanced Issues on ClassesAdvanced Issues on Classes

Part 3Reference variables (Tapestry pp.581, Horton 176 – 178)

Const-reference variables (Horton 176 – 178)

object sharing: sharing an object/variable among several objects using reference variables and pointers (Tapestry 578 – 583)

Static data members in classes (Tapestry 484 – 486, Horton 322 – 325)

Page 2: Advanced Issues on Classes

2

OverviewOverviewIn this part, you are going to see how different instances of a class (p1 and

p2 below) can share the same variable (board, below).

ChessBoard board;

ChessPiece p1, p2;

Normally, if we add a variable, board, to the ChessPiece class, there will be a different board for each ChessPiece. Of course, this is not what we want; we want a single board to shared by all ChessPiece objects.

There are two solutions for this, one is to use what is called reference variables (this is very easy in term of its use, but slightly more tricky in some other aspects) or to use pointers (this is slightly more complicated to use, but very easy to understand and there are no issues to consider).

Page 3: Advanced Issues on Classes

Reference VariablesReference VariablesIt is possible to refer one variable using another name (i.e. creating an alias)

using reference variables.

Syntax

Type & ref_variable_name = existing_variable;

int x;

int & xr = x;

No new memory allocation is done for xr.

x and xr refer to the same memory locations after the reference variable declaration. Thus when we say x, it means xr and when we say xr, it is also x.

This does not seem very useful now, but will be so when you want to share some data among multiple instances of a class (see shared-unshared toy example). There are some other useful cases as well.

– But before that we will see more on reference variables.

x xr

Page 4: Advanced Issues on Classes

Reference VariablesReference Variables

The value of reference b cannot be changed after its declaration. – For example, a few lines further, you cannot write:

double c = 2.71;&b = c;

– expecting now b is c. This is a syntax error (let's see this at refvar.cpp)– Everything is said on the declaration line of b: Reference b and variable a are

bound together, not to be ever separated.

What about the following addition to the end of code above? Does it change the values a, b, c?

double c = 2.71;b = c;

– Let's see the answer and another special case on the code (refvar.cpp)

a contains: 89

Example (refvar. cpp)double a = 3.1415927; double &b = a; b = 89; cout << "a contains: " << a << endl; // Displays ?

Page 5: Advanced Issues on Classes

Reference VariablesReference Variables and Pointers and PointersSo, are reference variables same as pointers?

– Similar concepts but reference variables are NOT pointers.– There are differences.

Pointers need to be dereferenced using the * operator to reach the memory location, but reference variables are already dereferenced.

int * ptr = new int;* ptr = 10;cout << *ptr;

Pointers are more flexible to use. Pointers need not be initialized at declaration (they can be initialized later). Moreover, pointers can be reinitialized to show different locations throughout the program. However, reference variables must be initialized at declaration to an existing variable and cannot refer to another variable later.

Page 6: Advanced Issues on Classes

Const-Const-Reference VariablesReference VariablesReference variables can be constant, by putting the const keyword before the

variable declaration.

double x = 4.12;const double & y = x;

We already said that binding between reference variable and the existing regular variable cannot change. So, what is constant here?– It is constant such that you cannot change the memory location's value by

using the reference variable name.– But this does not mean that this memory location's value cannot change.– We can change it using other references (e.g. using the actual variable name)

– see refvar.cpp– We cannot assign a const-reference variable to a normal reference variable

double x = 4.12;const double & y = x;y = 3.5; //syntax error, const ref. var. cannot be changedx = 3.5; //legal syntaxdouble & z = y; //syntax error, const-ref. var. cannot be assigned to a regular ref. var.

Page 7: Advanced Issues on Classes

Reference Reference Parameters: Parameters: reminderreminderReference parameters are used to allow a function to modify a calling variable:

#include <iostream> using namespace std; void change (double & r, double s) {

r = 30; s = 40;

}

int main () { double k, m; k = 3; m = 4; change (k, m); cout << k << ", " << m << endl; // Displays ????return 0; }

Page 8: Advanced Issues on Classes

Reference Reference Parameters: reminderParameters: reminderReference parameters are used to allow a function to modify a calling variable:

#include <iostream> using namespace std; void change (double & r, double s) {

r = 30; s = 40;

}

int main () { double k, m; k = 3; m = 4; change (k, m); cout << k << ", " << m << endl; // Displays 30, 4return 0; }

Page 9: Advanced Issues on Classes

Pointers for sharingPointers for sharing

If you want to share the same object among various instances of a class object, you must use pointers or reference variables– You should prefer reference variables, if you do the binding at the

beginning and do not need to refer to another object during the program.

– Pointers are easier, if you feel comfortable on how to manipulate them.

One example of need of sharing is that of players (class player) sharing the same game board (class board) . We will not follow-up on this example.

In the next example that we will see, kids are intended to share the same toy (e.g. same ball that each of them plays with together), but in the naive version, each of them ends up having a separate ball.

Page 10: Advanced Issues on Classes

(Un)shared Toy– toy class (from Tapestry)(Un)shared Toy– toy class (from Tapestry)Naive version that does not actually workNaive version that does not actually work

#include <iostream>#include <string>using namespace std;

// This program demonstrates the use of reference variables and// pointers for sharing among class objectsclass Toy { public: Toy (const string& name); void Play(); // prints a message void Breaks(); // the toy breaks private: string myName; //name of toy bool isWorking; //working condition};

//constructorToy::Toy(const string & name) : myName(name), isWorking(true){ }

See sharetoy.cpp

Continued on the next page

Page 11: Advanced Issues on Classes

// post: toy is played with, message printedvoid Toy::Play(){

if (isWorking)cout << "this " << myName << " is so fun :-)" << endl;

elsecout << "this " << myName << " is broken :-(" << endl;

}

void Toy::Breaks()// post: toy is broken{ isWorking = false; cout <<endl<< "oops, this " << myName <<" just broke"<<endl<<endl;}

Page 12: Advanced Issues on Classes

(Un)shared Toy – kid class(Un)shared Toy – kid classclass Kid{ public: Kid (const string & name, Toy & toy); void Play(); private: string myName; //name of the kid Toy myToy; //creates a local copy, this is problematic };

//constructorKid::Kid(const string & name, Toy & toy) : myName(name), myToy(toy){ } //Equivalent to myToy = toy;

void Kid::Play()// post: kid plays and talks about it{ cout << "My name is " << myName << ", "; myToy.Play();}

Page 13: Advanced Issues on Classes

Does not work as intendedDoes not work as intended

int main(){

//lets have a !!common!! toy Toy atoy("ball");

Kid erich("erich", atoy); Kid katie("katie", atoy);

erich.Play(); katie.Play();

atoy.Breaks(); // the toy is now broken

// but this information is not // conveyed to the kids

erich.Play(); katie.Play(); //they don’t see that it is broken! return 0;

}

“erich”“ball”

working

“katie”“ball”

working

“ball”working

“ball”

broken

What Happens in Memory:

“erich”“ball”

working

“katie”“ball”

working

copy

---------- After atoy breaks ---------

Page 14: Advanced Issues on Classes

(Un)shared toy - output(Un)shared toy - output

Why it did not work out?– Kid class stores a local copy of the Toy object– When we change the state of the source Toy object, it did not

effect the Kid class– Basically, Kid did not share a toy, just cloned it.

Page 15: Advanced Issues on Classes

Solution 1: Reference VariablesSolution 1: Reference VariablesSimilar to reference parameters (alias for memory allocated elsewhere)

reference variables refers to memory allocated elsewhere.

– making myToy a reference variable avoids creating a copy when myToy is initialized in the Kid initializer list.

• the Toy object created before the Kid objects are used while constructing the Kid objects

– once a reference variable is constructed, it cannot be reassigned to another object

• a Kid object cannot change toys

instance variables that are references must be constructed and initialized in initializer lists, not in the body of class

constructors

Page 16: Advanced Issues on Classes

Sharing with Reference VariablesSharing with Reference Variablesclass Kid

{

public:

Kid (const string & name, Toy & toy);

void Play();

private:

string myName;

Toy & myToy;

};

//the rest is the same as NAIVE version

Kid::Kid(const string & name,Toy & toy)

: myName(name), myToy(toy)

{ }

.

.

.

Same main just works fine!

int main(){

Toy atoy("ball");Kid erich("erich",

atoy);Kid katie("katie",

atoy);

erich.Play(); katie.Play();

atoy.Breaks();

erich.Play();

katie.Play();

return 0;}

Page 17: Advanced Issues on Classes

Sharing with Reference VariablesSharing with Reference Variables

Page 18: Advanced Issues on Classes

Sharing with PointersSharing with Pointers

You can achieve the same result by using a pointer (to a toy object defined outside the class), as shown in the following slides.

However, the reference variable makes sharing very simple:– Just add & at the beginning of variable name

Page 19: Advanced Issues on Classes

Solution 2:Sharing with PointersSolution 2:Sharing with Pointersclass Kid{public:Kid (const string & name, Toy * toy);void Play();

private:string myName;Toy * myToy;

};

//constructorKid::Kid(const string & name, Toy * toy) : myName(name), myToy(toy){ }

void Kid::Play()// post: kid plays and talks about it{cout << "My name is " << myName <<", ";myToy->Play();

}

“ball”working

atoy

myToy

int main(){

Toy atoy("ball");Kid erich("erich", &atoy);Kid katie("katie", &atoy);

erich.Play(); katie.Play();atoy.Breaks();

erich.Play(); katie.Play();return 0;

}

Page 20: Advanced Issues on Classes

Sharetoy.cpp (all 3 approaches)Sharetoy.cpp (all 3 approaches)Three preprocessor identifiers NAIVE, REFVAR, POINTER are used (at any time only one

of them should be defined to run the corresponding approach)The definiton of class Toy is the same in three approaches. Only Kid class and main are

different. These are differentiated using preprocessor directives.

Kid Class Definitions:

#if defined(NAIVE)

// NAIVE version of Kid class comes here

. . .

#elif defined(REFVAR)

// Reference variable version of Kid class comes here

. . .

#elif defined(POINTER)

// Pointer version of Kid class comes here

. . .

#endif

Page 21: Advanced Issues on Classes

Sharetoy.cpp (all 3 approaches)Sharetoy.cpp (all 3 approaches)Main function:

int main(){

//lets have a toy Toy atoy("ball");

#if defined(NAIVE) || defined(REFVAR) //Naive and Ref. var. versions are the same

Kid erich("erich", atoy); Kid katie("katie", atoy);

erich.Play(); katie.Play(); atoy.Breaks(); // the toy is now broken erich.Play(); katie.Play();

#elif defined(POINTER) Kid erich("erich", &atoy);

Kid katie("katie", &atoy); erich.Play(); katie.Play();

atoy.Breaks(); // the toy is now broken erich.Play(); katie.Play();

#endif

return 0;}

Page 22: Advanced Issues on Classes

LinkStringSetLinkStringSetIterator Class - RevisitedIterator Class - RevisitedAppend " seed" to the end of the info field of

each node in the list.

LinkStringSet c;

c.insert("watermellon");

c.insert("apricot");

LinkStringSetIterator itr(c);

for (itr.Init(); itr.HasMore(); itr.Next())

{

itr.Current().append(" seed");

}

cout << "after update\nc : ";

Print(c);

We have seen that the code on the left works properly when the function Current() return a reference.

string & Current() const { return myCurrent->info; }

How can we make use reference variables here instead of changing the function return value directly?

We may store the return value of Current() in a ref. variable and change it.

See next slide

after updatec : apricot seed watermelon seed

---------- size = 2

Page 23: Advanced Issues on Classes

LinkStringSetLinkStringSetIterator Class - RevisitedIterator Class - RevisitedLinkStringSet c;

c.insert("watermellon");

c.insert("apricot");

LinkStringSetIterator itr(c);

for (itr.Init(); itr.HasMore(); itr.Next())

{

string &currentInfo= itr.Current();

currentInfo.append(" seed");

}

cout << "after update\nc : ";

Print(c);

What's output?

NOT IN THE BOOKS

See linkedstringset.h and .cpp, and linksetdemo.cppfor details

after updatec : apricot seed watermelon seed

---------- size = 2

The definition of Current() is still the same; it return a reference

string & Current() const { return myCurrent->info; }

The body of the iterator loop changes as shown on the right.

Thinking Further: How would you use pointers instead of reference variables?

Page 24: Advanced Issues on Classes

24

Related: Static Data Member of the ClassRelated: Static Data Member of the ClassIn certain cases, you can also use a static data member of the class that keeps its value

from one construction to the next one.– When a data member is declared as static, only one copy of the data is maintained for all

objects of the class.– Static data members are not part of objects of a given class type; they are separate objects.

As a result, the declaration of a static data member is not considered a definition. The data member is declared in class scope, but definition is performed at file scope.

You can also use static data members to count the number of instances of a class that you created by calling the constructor.

The example below assigns a consecutive ID number to the class instances class User{

private: int id; //id of user static int next_id; //common to all instances, but this is not a definition

//this is just a prototype-like declarationpublic: User() //constructs user instances starting from id number 1 { id = next_id; next_id++; //assigns next id to the next instance and then increments } void print () { cout << id << endl; }

};int User::next_id = 1; //this is definition and initialization

See static_data_member.cpp