Class. Sams Publishing Object-Oriented Programming in C++
Reklaam
Reklaam
12 3087 CH11 11/29/01 2:20 PM Page 540
540
Chapter 11
In object diagrams, objects are represented by rectangles, just as classes are in class diagrams.
The object’s name, attributes, and operations are shown in a similar way. However, objects are distinguished from classes by having their names underlined. Both the object name and the class name can be used, separated by a colon: anObj:aClass
If you don’t know the name of the object (because it’s only known through a pointer, for example) you can use just the class name preceded by the colon:
:aClass
Lines between the objects are called links, and represent one object communicating with another. Navigability can be shown. The value of an attribute can be shown using an equal sign: count = 0
Notice there’s no semicolon at the end; this is the UML, not C++.
Another UML feature we’ll encounter is the note. Notes are shown as rectangles with a dogeared (turned down) corner. They hold comments or explanations. A dotted line connects a note to the relevent element in the diagram. Unlike associations and links, a note can refer to an element inside a class or object rectangle. Notes can be used in any kind of UML diagram.
We’ll see a number of object diagrams in the balance of this chapter.
A Memory-Efficient
String
Class
The
ASSIGN and
XOFXREF examples don’t really need to have overloaded assignment operators and copy constructors. They use straightforward classes with only one data item, so the default assignment operator and copy constructor would work just as well. Let’s look at an example where it is essential for the user to overload these operators.
Defects with the
String
Class
We’ve seen various versions of our homemade
String class in previous chapters. However, these versions are not very sophisticated. It would be nice to overload the
= operator so that we could assign the value of one
String object to another with the statement s2 = s1;
If we overload the
= operator, the question arises of how we will handle the actual string (the array of type char
), which is the principal data item in the
String class.
One possibility is for each
String object to have a place to store a string. If we assign one
String object to another (from s1 into s2 in the previous statement), we simply copy the string from the source into the destination object. If you’re concerned with conserving memory, the
12 3087 CH11 11/29/01 2:20 PM Page 541
Virtual Functions
541 problem with this is that the same string now exists in two (or more) places in memory. This is not very efficient, especially if the strings are long. Figure 11.4 shows how this looks.
str s1:String
“This is a long string in memory.”
11
str s2:String
“This is a long string in memory.”
F
IGURE
11.4
UML object diagram: replicating strings.
Instead of having each
String object contain its own char* string, we could arrange for it to contain only a pointer to a string. Now, if we assign one
String object to another, we need only copy the pointer from one object to another; both pointers will point to the same string.
This is efficient, since only a single copy of the string itself needs to be stored in memory.
Figure 11.5 shows how this looks.
str s1:String
“This is a long string in memory.” str s2:String
F
IGURE
11.5
UML object diagram: replicating pointers to strings.
However, if we use this system we need to be careful when we destroy a
String object. If a
String
’s destructor uses delete to free the memory occupied by the string, and if there are several objects with pointers pointing to the string, these other objects will be left with pointers pointing to memory that may no longer hold the string they think it does; they become dangling pointers.
12 3087 CH11 11/29/01 2:20 PM Page 542
542
Chapter 11
To use pointers to strings in
String objects, we need a way to keep track of how many
String objects point to a particular string, so that we can avoid using delete on the string until the last
String that points to it is itself deleted. Our next example,
STRIMEM
, does just this.
A String-Counter Class
Suppose we have several
String objects pointing to the same string and we want to keep a count of how many
String s point to the string. Where will we store this count?
It would be cumbersome for every
String object to maintain a count of how many of its fellow
String s were pointing to a particular string, so we don’t want to use a member variable in
String for the count. Could we use a static variable? This is a possibility; we could create a static array and use it to store a list of string addresses and counts. However, this requires considerable overhead. It’s more efficient to create a new class to store the count. Each object of this class, which we call strCount
, contains a count and also a pointer to the string itself. Each
String object contains a pointer to the appropriate strCount object. Figure 11.6 shows how this looks.
psc s1:String
:strCount count = 3 str
“This is a long string in memory.” psc s2:String psc s3:String psc s4:String psc s5:String
F
IGURE
11.6
String
and
strCount
objects.
:strCount count = 2 str
“This is a different long string.”
12 3087 CH11 11/29/01 2:20 PM Page 543
Virtual Functions
543
To ensure that
String objects have access to strCount objects, we make
String a friend of strCount
. Also, we want to ensure that the strCount class is used only by the
String class.
To prevent access to any of its functions, we make all member functions of strCount private.
Because
String is a friend
, it can nevertheless access any part of strCount
. Here’s the listing for
STRIMEM
:
// strimem.cpp
// memory-saving String class
// overloaded assignment and copy constructor
#include <iostream>
#include <cstring> //for strcpy(), etc.
using namespace std;
//////////////////////////////////////////////////////////////// class strCount //keep track of number
{ //of unique strings private: int count; //number of instances char* str; //pointer to string friend class String; //make ourselves available
//member functions are private
//-------------------------------------------------------------strCount(char* s) //one-arg constructor
{ int length = strlen(s); //length of string argument str = new char[length+1]; //get memory for string strcpy(str, s); //copy argument to it count=1; //start count at 1
}
//--------------------------------------------------------------
~strCount() //destructor
{ delete[] str; } //delete the string
};
//////////////////////////////////////////////////////////////// class String //String class
{ private: strCount* psc; //pointer to strCount public:
String() //no-arg constructor
{ psc = new strCount(“NULL”); }
//--------------------------------------------------------------
String(char* s) //1-arg constructor
{ psc = new strCount(s); }
//--------------------------------------------------------------
String(String& S) //copy constructor
{
11
12 3087 CH11 11/29/01 2:20 PM Page 544
544
Chapter 11 psc = S.psc;
(psc->count)++;
}
//--------------------------------------------------------------
~String() //destructor
{ if(psc->count==1) //if we are its last user, delete psc; // delete our strCount else // otherwise,
(psc->count)--; // decrement its count
}
//-------------------------------------------------------------void display() //display the String
{ cout << psc->str; //print string cout << “ (addr=” << psc << “)”; //print address
}
//-------------------------------------------------------------void operator = (String& S) //assign the string
{ if(psc->count==1) //if we are its last user, delete psc; // delete our strCount else // otherwise,
(psc->count)--; // decrement its count psc = S.psc; //use argument’s strCount
(psc->count)++; //increment its count
}
};
//////////////////////////////////////////////////////////////// int main()
{
String s3 = “When the fox preaches, look to your geese.”; cout << “\ns3=”; s3.display(); //display s3
String s1; //define String s1 = s3; //assign it another String cout << “\ns1=”; s1.display(); //display it
String s2(s3); //initialize with String cout << “\ns2=”; s2.display(); //display it cout << endl; return 0;
}
12 3087 CH11 11/29/01 2:20 PM Page 545
Virtual Functions
545
In the main() part of
STRIMEM we define a
String object, s3
, to contain the proverb “When the fox preaches, look to your geese.” We define another
String s1 and set it equal to s3
; then we define s2 and initialize it to s3
. Setting s1 equal to s3 invokes the overloaded assignment operator; initializing s2 to s3 invokes the overloaded copy constructor. We print out all three strings, and also the address of the strCount object pointed to by each object’s psc pointer, to show that these objects are all the same. Here’s the output from
STRIMEM
: s3=When the fox preaches, look to your geese. (addr=0x8f510e00) s1=When the fox preaches, look to your geese. (addr=0x8f510e00) s2=When the fox preaches, look to your geese. (addr=0x8f510e00)
The other duties of the
String class are divided between the
String and strCount classes.
Let’s see what they do.
The
strCount
Class
The strCount class contains the pointer to the actual string and the count of how many
String class objects point to this string. Its single constructor takes a pointer to a string as an argument and creates a new memory area for the string. It copies the string into this area and sets the count to 1, since just one
String points to it when it is created. The destructor in strCount frees the memory used by the string. (We use delete[] with brackets because a string is an array.)
The
String
Class
The
String class uses three constructors. If a new string is being created, as in the zeroargument and C-string-argument constructors, a new strCount object is created to hold the string, and the psc pointer is set to point to this object. If an existing
String object is being copied, as in the copy constructor and the overloaded assignment operator, the pointer psc is set to point to the old strCount object, and the count in this object is incremented.
The overloaded assignment operator, as well as the destructor, must also delete the old strCount object pointed to by psc if the count is 1. (We don’t need brackets on delete because we’re deleting only a single strCount object.) Why must the assignment operator worry about deletion? Remember that the
String object on the left of the equal sign (call it s1
) was pointing at some strCount object (call it oldStrCnt
) before the assignment. After the assignment s1 will be pointing to the object on the right of the equal sign. If there are now no
String objects pointing to oldStrCnt
, it should be deleted. If there are other objects pointing to it, its count must be decremented. Figure 11.7 shows the action of the overloaded assignment operator, and Figure 11.8 shows the copy constructor.
11
12 3087 CH11 11/29/01 2:20 PM Page 546
546
Chapter 11 psc s3:String :strCount count = 1 str psc s1:String
Before execution of s1 = s2;
:strCount count = 1 str psc s3:String :strCount count = 2 str psc s1:String
After execution of s1 = s2;
F
IGURE
11.7
Assignment operator in
STRIMEM
.
psc s3:String :strCount count = 1 str
Before execution of String s2 (s3); psc s3:String :strCount count = 2 str psc s2:String
After execution of String s2 (s3);
F
IGURE
11.8
Copy constructor in
STRIMEM
.
“When the fox preaches…”
“NULL”
“When the fox preaches…”
“When the fox preaches…”
“When the fox preaches…”
Laadi alla
Reklaam