[ Pobierz całość w formacie PDF ]
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
file:///D|/Cool Stuff/old/ftp/1/1/ch05/ch05.htm (13 von 29) [12.05.2000 14:46:09]
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 5 - Object-Oriented Programming and Design
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
file:///D|/Cool Stuff/old/ftp/1/1/ch05/ch05.htm (14 von 29) [12.05.2000 14:46:09]
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 5 - Object-Oriented Programming and Design
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; }
file:///D|/Cool Stuff/old/ftp/1/1/ch05/ch05.htm (15 von 29) [12.05.2000 14:46:09]
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 5 - Object-Oriented Programming and Design
//...
};
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
//were Base's destructor virtual, the correct destructor would be called
return 0;
}
Virtual Member Functions
Virtual member functions enable subclasses to extend or override the behavior of a base class. Deciding which
members in a class can be overridden by a derived class is not a trivial issue. A class that overrides a virtual
member function is only committed to adhere to the prototype of the overridden member function -- not to its
implementation. A common mistake is to declare all member functions as virtual "just in case". In this
respect, C++ makes a clear-cut distinction between abstract classes that provide pure interfaces as opposed to
base classes that provide implementation as well as an interface.
Extending A Virtual Function in A Derived Class
There are cases in which you want a derived class to extend a virtual function defined in its base class rather than
override it altogether. It can be done quite easily in the following way:
class shape
{
//...
public:
virtual void draw();
virtual void resize(int x, int y) { clearscr(); /*...*/ }};
class rectangle: public shape
{
//...
public:
virtual void resize (int x, int y)
{
shape::resize(x, y); //explicit call to the base's virtual function
//add functionality
int size = x*y;
//...
}
};
The overriding function in a derived class should invoke an overridden function of its base class using its
file:///D|/Cool Stuff/old/ftp/1/1/ch05/ch05.htm (16 von 29) [12.05.2000 14:46:09]
ANSI/ISO C++ Professional Programmer's Handbook - Chapter 5 - Object-Oriented Programming and Design
fully-qualified name.
Changing Access Specification of A Virtual Function
The access specification of a virtual member function that is defined in a base class can be changed in a
derived class. For example
class Base
{
public:
virtual void Say() { cout
};
class Derived : public Base
{
private: //access specifier changed; legal but not a good idea
void Say() {cout
};
Although this is legal, it does not work as expected when pointers or references are used; a pointer or reference to
Base can also be assigned to any object that is publicly derived from Base:
Derived d;
Base *p = &d;
p->Say(); //OK, invokes Derived::Say()
Because the actual binding of a virtual member function is postponed to runtime, the compiler cannot detect that
a nonpublic member function will be called; it assumes that p points to an object of type Base, in which Say()
is a public member. As a rule, do not change the access specification of a virtual member function in a derived
class.
Virtual Member Functions Should Not Be Private
As you saw previously, it is customary to extend virtual functions in a derived class by first invoking the base
class's version of that function; then extend it with additional functionality. This can't be done when a virtual
function is declared private.
Abstract Classes and Interfaces
An abstract class is one that has at least one pure virtual member function, that is, a non-implemented placeholder
that must be implemented by its derived class. Instances of an abstract class cannot be created because it is
[ Pobierz całość w formacie PDF ]