Then it sets the reference count of that object to 1, indicating that there is one MainObjectArray "handle" object using that "body" object.. One way to share data safely in such a situ
Trang 1another enclosing class called NewBlockPtr If that Block class had a Get member function, we could declare it as "char
NewBlockPtr::Block::Get(int p_Index);", and the compiler would have no problem telling which Block::Get we meant Similarly, an unadorned
"Block::Get(int p_Index);" would be yet another completely distinct function.20 One more safety feature in this new arrangement is worthy of note: the constructor for Block is protected, so that the user can't accidentally create one and use it instead of referencing it through a BlockPtr object the way we intend This isn't very likely anyway, since the user would have to specify
BlockPtr::Block as the type, which isn't easy to do by accident; however, according to the principles of object-oriented programming, it is best to hide the internals of our classes whenever possible, so that class user code can't depend
on those internals That restriction makes it possible for us to improve the
implementation of our classes without breaking user code
Polite Pointing
Now that we've investigated the notion of redefining operator->, let's return to the MainObjectArrayPtr class that we were discussing a little while ago We'll start with Figure newquant.04, which shows the code for the normal
constructor for that class
The normal constructor for MainObjectArrayPtr (from quantum\newquant.cpp) (Figure newquant.04)
codelist/newquant.04
This function is fairly simple It creates a new MainObjectArray object using the normal constructor for that class This is the "body" object that will actually
do the work of both of these classes Then it sets the reference count of that
object to 1, indicating that there is one MainObjectArray "handle" object using that "body" object
The general idea of reference counting is fairly simple, as most great ideas are (after you understand them, at least) It's inefficient to copy a lot of data whenever
we set one variable to the same value as another; on the other hand, copying a pointer to the data is much easier However, we have to consider how we will know when we can delete the data being pointed to, which will be when no one needs the data anymore
Trang 2One way to share data safely in such a situation is to write the constructor(s),
destructor, and assignment operator to keep track of the number of objects that are using a particular body object, and when there are no more users of that object, to delete it.21 We'll see how this works in the case of the MainObjectArray body class and its MainObjectArrayPtr handle class; the implementation is much the same in the other handle/body classes in this program
However, right now we're more interested in how we can use operator-> in the FlexArray::Open function (Figure newquant.02) After creating a member variable called m_MOA via the normal constructor of the
MainObjectArrayPtr class, the next line in the Open function invokes MainObjectArrayPtr::operator-> (Figure newquant.06)
MainObjectArrayPtr::operator-> (from quantum\newquant.cpp) (Figure
newquant.06)
codelist/newquant.06
Given the definition of operator-> in Figure newquant.06, how will the
compiler interpret the expression m_MOA->FindObjectByName, which is used
in the function FlexArray::Open?
1 Since m_MOA is not a pointer to any type, the interface of its class
(MainObjectArrayPtr) is examined for a definition of operator->
2 Since there is such a definition, a call to that code is inserted in the function being compiled
3 Then the return value of the operator-> code is examined Since it is a
pointer type (MainObjectArray *, to be exact), the compiler continues by generating code to call MainObjectArray->FindObjectByName with this equal to the result returned by operator->, which is the address of the body object of the MainObjectArrayPtr that we started with Of course, the same analysis applies to the other uses of the m_MOA object in the Open function
as well as the other functions that use handle objects
Now let's continue with the analysis of the Open function in the FlexArray class The statement we've just examined calls a function called
FindObjectByName, which returns an index into the main object array If the name supplied in the call was in fact the name of an existing object in the main
Trang 3object array, then the returned value will be the element number of that object in the main object array On the other hand, if the specified name was not found in the main object array, the returned value will be the special value NoObject In that case, Open will use another member function of MainObjectArray,
namely FindAvailableObject, to locate a free element in the main object array Then it will call the CreateMainObject function to create a new main object which will use that free element
At this point in the function, we have a valid index into the main object array for our FlexArray, whether it existed previously or was just created Finally, we retrieve the current and maximum element counts for the FlexArray object
Now that we have covered the Open function in the FlexArray class, there's only one other function in that class that could use a significant amount of
explanation: the implementation of operator [] However, I'm going to
postpone coverage of that function until the next chapter, where we'll be examining the somewhat complex task of making one of our data types look as much like an array as possible
In the meantime, we should go over the rest of the member functions of the
MainObjectArrayPtr class, so you can see how this typical handle class works
The MainObjectArrayPtr class
We'll start with the default constructor (Figure newquant.07), which is pretty
simple In fact, it's exactly like the normal constructor (Figure newquant.04),
except that it uses the default constructor of its body class to create its body object rather than the normal constructor of the body class
The default constructor for MainObjectArrayPtr (from quantum\newquant.cpp) (Figure newquant.07)
codelist/newquant.07
The MainObjectArrayPtr Copy Constructor
The copy constructor (Figure newquant.08) isn't much more complicated It copies the pointer to the main object array (body) object from the existing object to the
Trang 4same member variable in the newly created object Then it increments the
reference count because there is now one more user of that body object
The copy constructor for MainObjectArrayPtr (from quantum\newquant.cpp) (Figure newquant.08)
codelist/newquant.08
The MainObjectArrayPtr::operator = Function
The next function, MainObjectArrayPtr::operator = (Figure
newquant.09), is a bit more complicated than the previous ones, because it has to account for the reference counts in both the current object and the right-hand object being copied from
MainObjectArrayPtr::operator= (from quantum\newquant.cpp) (Figure
newquant.09)
codelist/newquant.09
We start by decrementing the reference count of the current body object, because
we won't be pointing to it after this operation (unless the right-hand object happens
to point to the same body object as ours does) Then we check whether the
reference count is less than or equal to 0, in which case we can (and will) delete the current body object.22 After taking care of that issue, we copy the right-hand
object's main object array pointer to the corresponding member variable in our object and increment the reference count for that main object array Finally, we return our object to the caller
The MainObjectArrayPtr Destructor
The final function in the MainObjectArrayPtr class is the destructor
(Figure newquant.10)
The destructor for the MainObjectArrayPtr class (from quantum\newquant.cpp) (Figure newquant.10)
codelist/newquant.10
This shouldn't seem at all mysterious after our analysis of the operator =
function, as its operation is basically a subset of what that function does That is, it decrements the reference count of its body object; if the resulting reference count is less than or equal to 0, it deletes the body object
Trang 5Now that we're through with the MainObjectArrayPtr class, we'll examine its body class, MainObjectArray, so we can see how this fundamental data type of the quantum file algorithm works.23
The MainObjectArrayPtr::MainObjectArray class
We'll skip over the default constructor and destructor, as these functions don't do anything particularly interesting However, the normal constructor for this class does some initialization that we should pay attention to, as shown in Figure
newquant.11
The normal constructor for the MainObjectArray class (from
quantum\newquant.cpp) (Figure newquant.11)
codelist/newquant.11
As you can see, this constructor begins by checking whether its argument is non-null; if so, it continues by initializing a member variable to the quantum file pointer provided by that argument Then it uses that quantum file pointer to retrieve the number of main objects in the quantum file as well as the number of blocks that the main object list occupies Then it resizes its block pointer list to that count and initializes each of the elements in the list to the appropriate block pointer We'll see exactly how those block pointers work when we get back to the QuantumFile class, but for now we'll just assume that they allow access to the individual blocks that make up the main object list
The MainObjectArray::Get Function
Now let's take a look at the Get function in this class (Figure newquant.12)
The MainObjectArray::Get function (from quantum\newquant.cpp) (Figure newquant.12)
codelist/newquant.12
This function is reasonably straightforward It begins by checking the index of the element in the main object array that the caller wants to retrieve If it is out of range, the return value is NoObject, indicating an error However, assuming that the index is valid, the function calculates which block and element within that block corresponds to the requested entry It then retrieves that element via the MainObjectBlock::Get function, which is called for the m_BlockPtr
Trang 6variable for that block.24 Finally, the result of the Get function, which is the
quantum number of the big pointer quantum of the object in question, is returned to the calling function
The MainObjectArray::Set Function