[ Pobierz całość w formacie PDF ]
instance thereof. There are several features for manipulating the state of a class rather
than the state of individual objects. These features are discussed in the following sections.
Static Data Members
A static member is shared by all instances of its class. For that reason, it is sometimes
termed a class variable. Static members are useful in synchronization objects. For
example, a file lock can be implemented using a static data member. An object that is
trying to access this file has to check first whether the file is being processed by another
user. If the file is available, the object turns the flag on and user can process the file
safely. Other users are not allowed to access the file until the flag is reset to false. When
the object that is processing the file is finished, it has to turn off the flag, enabling another
object to access it.
class fileProc
{
private:
FILE *p;
static bool Locked;
public:
//...
bool isLocked () const;
//...
};
bool fileProc::Locked;
Static Member Functions
A static member function in a class can access only other static members of its class..
Unlike ordinary member functions, a static member function can be invoked even when
no object instance exists. For example
class stat
{
private:
int num;
public:
stat(int n = 0) {num=n;}
static void print() {cout
};
int main()
{
stat::print(); //no object instance required
stat s(1);
s.print();//still, a static member function can be called from an
object
return 0;
}
Static members are used in the following cases:
" When all other data members of an object are also static
" When the function does not depend on any other object member (like print(), in
the previous example)
" As a wrapper of a global function
A Pointer to Member Cannot Refer To a Static Member Function
It is illegal to assign the address of a static class member to a pointer to member.
However, you can take the address of a static member function of a class and treat it as if
it were an ordinary function. For example
class A
{
public:
static void f();
};
int main()
{
void (*p) () = &A::f; //OK, ordinary pointer to function
}
You can do this because a static member function is essentially an ordinary function,
which doesn't take an implicit this argument.
Defining a Class Constant
When you need a constant integer member in a class, the easiest way to create one is by
using a const static member of an integral type; unlike other static data members,
such a member can be initialized within the class body (see also Chapter 2, "Standard
Briefing: The Latest Addenda to ANSI/ISO C++"). For example
class vector
{
private:
int v_size;
const static int MAX 1024; //a single MAX is shared by all vector
objects
char *p;
public:
vector() {p = new char[MAX]; }
vector( int size)
{
if (size
p = new char[size] ;
else
p = new char[MAX];
}
};
Designing Class Hierarchies
After identifying a set of potential classes that might be required for the application, it is
important to correctly identify the interactions and relationships among the classes to
specify inheritance, containment, and ownership. The design of class hierarchies, as
opposed to designing concrete types, requires additional considerations that are discussed
in this section.
Private Data Members Are Preferable To Protected Ones
Data members of a class are usually a part of its implementation. They can be replaced
when the internal implementation of the class is changed; therefore, they need to be
hidden from other classes. If derived classes need to access these data members, they
need to use accessor methods instead of directly accessing data members of a base class.
Consequently, no modification is required for derived classes when a change is made in
the base class.
Here's an example:
class Date
{
private:
int d,m,y //how a date is represented is an implementation detail
public:
int Day() const {return d; }
};
class DateTime : public Date
{
private:
int hthiss;
int minutes;
int seconds;
public:
//...additional member functions
};
Now assume that class Date is used mostly on display devices, so it has to supply some
method of converting its d,m,y members into a displayable string. In order to enhance
performance, a design modification is made: Instead of the three integers, a single
string now holds the date representation. Had class DateTime relied on the internal
implementation of Date, it would have had to be modified as well. But because it can
access Date's data members only through access methods, all that is required is a small
change in the Date::Day() member function. Please note that accessor methods are
usually inlined anyway, so their use does not incur additional runtime overhead.
Declaring Virtual Base Class Destructors
A base class needs to have its destructor declared virtual. In doing so, you ensure that
the correct destructor is always called, even in the following case:
class Base
{
private:
char *p;
public:
Base() { p = new char [200]; }
~ Base () {delete [] p; } //non virtual destructor, bad
};
class Derived : public Base
{
private:
char *q;
public:
Derived() { q = new char[300]; }
~Derived() { delete [] q; }
//...
};
void destroy (Base & b)
{
delete &b;
}
int main()
{
Base *pb = new Derived(); //200 + 300 bytes allocated
//... meddle with pb
destroy (*pb); //OOPS! only the destructor of Base is called
[ Pobierz całość w formacie PDF ]