Essential C++ 4_Object-Based Programming

    Before we can use a class, we must make it known to the program because the class is not built into the language. Usually, we do this by including aheader file:

#include <string> 
string pooh[ 4 ] = 
    { "winnie", "robin", "eeyore", "piglet" }; 

   The class name serves as a type name in the same way as the built-in type names such asint anddouble. Often, there are multiple ways to initialize an object of a class:

#include <vector> 

string dummy( "dummy" ); 
vector< string > svec1( 4 ); 
vector< string > svec2( 4, dummy ); 
vector< string > svec3( pooh, pooh+4 );

    Each class provides a set of operations we can apply to objects of the class. These operations typically consist of named functions, such assize() andempty(), and overloaded instances of the predefined operators, such as inequality and assignment:

    In general, a class consists of two parts: a public set of operations and operators, and a private implementation. These operations and operators are called classmember functions and represent thepublic interface of the class. As users of a class, we can access only the public interface. This, in fact, is how we have used the string class, the vector class, and so on. For example, all we know about thesize() member function of string is its prototype: It has a void parameter list and returns an integer value.

4.1 How to Implement a Class

    A class declaration begins with the keyword class followed by a user-specified class name:

class Stack;

    The class definition is necessary before we can define an actual Stack class object or refer to any members of the class. The skeleton of a class definition looks like this:

class Stack { 
public: 
   // ... public interface 
private: 
   // ... private implementation 
}; 

    The definition of a class consists of the class declaration followed by the class body enclosed in curly braces and terminated by a semicolon. Thepublic andprivate keywords within the class body control access to the members declared within each section. 

    Public members can be accessed from anywhere within the program. Private members can be accessed only by the member functions andfriends of the class � later I explain what a friend is (or at least what a friend within the C++ language is). Here is the beginning of our Stack class definition:

class Stack { 
public: 
   // each operation returns true if able to be carried out 
   // pop and peek place the string value within elem 
   bool   push( const string& ); 
   bool   pop(  string &elem ); 
   bool   peek( string &elem ); 

   bool   empty(); 
   bool   full(); 

   // definition of size() is placed within class 
   // other members are simply declared ... 
   int    size() { return _stack.size(); } 
private: 
   vector<string> _stack; // specific implementation
}; 

    Our Stack class definition supports the six operations we identified at the start of this section. The elements themselves are stored in a vector of strings we've named_stack. (My coding convention is to prepend data members with an underscore.)

    All member functions must be declared within the class definition. Optionally, a member function can also be defined inside the class definition. If defined within the body of the class, the member function is automatically treated as beinginline.size(), for example, is an inline member of Stack.

    To define a member function outside the class definition, we use a special declaration syntax. Its purpose is to identify the function as a member of a particular class. If the function is intended to be inline, theinline keyword must be specified:

inline bool 
Stack::empty() 
{ 
    return _stack.empty(); 
} 

    There is no difference in the treatment of an inline function if it is defined within or outside the class definition. As with the definition of a nonmember inline function, an inline member function should be placed in a header file.The class definition and the inline member functions are typically placed in a header file given the name of the class. For example, the Stack class definition and the definition ofempty() would be placed inside a header file namedStack.h. This is what the user includes whenever he wishes to use our class.

    The non-inline member functions are defined within a program text file, usually given the name of the class followed by one of the following suffixes:.C,.cc,.cpp, and.cxx (thex represents the reclining+). Microsoft Visual C++, for example, uses.cpp by default. 

4.2 What Are Class Constructors and the Class Destructor?(构造函数与析构函数)

    Each of our numeric sequences is a good candidate for a class. A numeric sequence class object represents a range of elements within its associated sequence. By default, the beginning position is 1. For example,

Fibonacci fib1( 7, 3 ); 

defines a Fibonacci class object of 7 elements beginning at position 3, and

Pell pel( 10 ); 

defines a Pell class object of 10 elements beginning at the default position of 1. Finally,

Fibonacci fib2( fib1 ); 

initializes fib2 to a copy of fib1.

    Each class must keep track both of its length � how many elements of the series are represented � and of a beginning position. A 0 or negative beginning position or length is not permitted. We store both the length and the beginning position as integers. For the moment, we define a third member, _next, which keeps track of the next element to iterate over:

class Triangular { 
public: 
   // ... 
private: 
   int _length; // number of elements 
   int _beg_pos; // beginning position of range    // date member
   int _next;    // next element to iterate over 
}; 
How do these data members get initialized?  if we provide one or more special initialization functions, the compiler does invoke the appropriate instance each time a class object is defined. These special initialization functions are called constructors.
    We identify a constructor by giving it the same name as the class.The syntactic rules are that the constructor must not specify a return type nor return a value. It can be overloaded. For example, here are three possible Triangular class constructors:

class Triangular {    
public: 
   // overloaded set of constructors 
   Triangular(); // default constructor 
   Triangular( int len ); 
   Triangular( int len, int beg_pos ); 

   // ... 
}; 
A constructor is invoked automatically based on the values supplied to the class object being defined. For example,
Triangular t; 
causes the default constructor to be applied to t. Similarly,
Triangular t2( 10, 3 ); 
causes the two-argument constructor to be applied. The values in parentheses are treated as the values to be passed to the constructor. 
    The simplest constructor is thedefault constructor. A default constructor requires no arguments. This means one of two things. Either it takes no arguments:
Triangular::Triangular() 
{   // default constructor 
    _length = 1; 
    _beg_pos = 1; 
    _next = 0; 
} 
   or, more commonly, it provides a default value for each parameter:
class Triangular { 
public: 
   // also a default constructor 
   Triangular( int len = 1, int bp = 1 ); 
   // ... 
}; 
Triangular::Triangular( int len, int bp ) 
{ 
    // _length and _beg_pos both must be at least 1 
    // best not to trust the user to always be right 
    _length = len > 0 ? len : 1; 
    _beg_pos = bp > 0 ? bp  : 1; 
    _next = _beg_pos-1; 
} 
   Because we provide a default value for both integer parameters, the single default constructor instance supports the original three constructors:
Triangular tri1;         // Triangular::Triangular( 1, 1 ); 
Triangular tri2( 12 );   // Triangular::Triangular( 12, 1 ); 
Triangular tri3( 8, 3 ); // Triangular::Triangular( 8, 3 ); 
The Member Initialization List

    A second initialization syntax within the constructor definition uses themember initialization list:

Triangular::Triangular( const Triangular &rhs ) 
   : _length ( rhs._length  ), 
     _beg_pos( rhs._beg_pos ),_next( rhs._beg_pos-1 ) 
{} // yes, empty! 
   The member initialization list is set off from the parameter list by a colon. It is a comma-separated list in which the value to be assigned the member is placed in parentheses following the member's name; it looks like a constructor call.
    The member initialization list is used primarily to pass arguments to member class object constructors. For example, let's redefine the Triangular class to contain a string class member:
class Triangular { 
public: 
   // ... 
private: 
   string _name; 
   int    _next, _length, _beg_pos; 
}; 
    To pass the string constructor the value with which to initialize_name, we use the member initialization list. For example,
Triangular::Triangular( int len, int bp ) 
    : _name( "Triangular" ) 
{ 
    _length = len > 0 ? len : 1; 
    _beg_pos = bp > 0 ? bp  : 1; 
    _next = _beg_pos-1; 
} 
    Complementing(对立) the constructor mechanism is that of the destructor.  A destructor is a user-defined class member function that, if present, is applied automatically to a class object before the end of its lifetime. The primary use of a destructor is to free resources acquired within the constructor or during the lifetime of the object.
     A destructor is given the name of the class prefixed by a tilde (~). It must not specify a return value and must declare an empty parameter list. Because it has an empty parameter list, the class destructor cannot be overloaded.
    Consider the following Matrix class. Within its constructor,the new expression is applied to allocate an array of doubles from the heap. The destructor is used to free that memory:
class Matrix { 
public: 
   Matrix( int row, int col ) 
         : _row( row ), _col( col ) 
   { 
           // constructor allocates a resource 
           // note: no error checking is shown 
           _pmat = new double[ row * col ]; 
   } 


   ~Matrix() 
   { 
           // destructor frees the resource 
           delete [] _pmat; 
   } 


   // ... 


private: 
   int     _row, _col; 
   double *_pmat; 
}; 
    In effect, we have automated the heap memory management within the Matrix class through the definition of its constructor and destructor. 
Memberwise Initialization (成员逐一初始化)
     By default, when we initialize one class object with another, as in
Triangular tri1( 8 ); 
Triangular tri2 = tri1; 
    the data members of the class are copied in turn. In our example,_length, _beg_pos, and _next are copied in turn from tri1 to tri2. This is called default memberwise initialization.

4.3 What Are mutable andconst?

4.4 What Is the this Pointer?

4.5 Static Class Members

    A static data member represents a single, shared instance of that member that is accessible to all the objects of that class. In the following class definition, for example, we declare_elems as a static data member of the Triangular class:

class Triangular { 
public: 
   // ... 
private: 
   static vector<int> _elems; 
}; 

    Because only a single instance of a static data member exists, we must provide an explicit definition of that instance within a program text file. The definition looks like the global definition of an object except that its name is qualified with the class scope operator:

// placed in program text file, such as Triangular.cpp 
vector<int> Triangular::_elems;

An initial value, if desired, can also be specified:

int Triangular::_initial_size = 8;

The member functions of the class can access a static data member the same as if it were an ordinary data member:

Triangular::Triangular( int len, int beg_pos ) 
    : _length( len > 0 ? len : 1 ), 
      _beg_pos( beg_pos > 0 ? beg_pos : 1 ) 
{ 
   _next = _beg_pos-1; 
   int elem_cnt = _beg_pos + _length; 

   if ( _elems.size() < elem_cnt ) 
        gen_elements( elem_cnt ); 
} 

A const static int data member, such asbuf_size, next, is the one instance in which a class member can be explicitly initialized within its declaration:

class intBuffer { 
public: 
   // ... 
private: 
   static const int _buf_size = 1024; // ok 
   int _buffer[ _buf_size ];          // ok 
};
Static Member Functions

  is_elem(), however, does not access any nonstatic data members. Its operation is independent of any particular class object, and it would be convenient to invoke it as a freestanding function. We can't write

if ( is_elem( 8 )) ... 

    however, because there is no way for the compiler or reader to know whichis_elem() we want to invoke. Use of the class scope operator clears up that ambiguity:

if ( Triangular::is_elem( 8 )) ... 

    A static member function can be invoked independently of a class object in exactly this way. A member function can be declared as static only if it does not access any nonstatic class members. We make it static by prefacing its declaration within the class definition with the keyword static:

class Triangular { 
public: 
   static bool is_elem( int ); 
   static void gen_elements( int length ); 
   static void gen_elems_to_value( int value ); 
   static void display( int len, int beg_pos, ostream &os = cout ); 
   // ... 
private: 
   static const int   _max_elems = 1024; 
   static vector<int> _elems; 
   // ... 
}; 

    When defined outside the class body, the static keyword is not repeated (this is also true of static data members): 

    Here is an example of how we might independently invoke is_elem():

bool is_elem = Triangular::is_elem( ival ); 


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值