Something about C/C++
Notice:I am learning C++ and I write down this artical just want to prevent me form forgetting.Reproduced,please indicate the source!
With the exception of static const int data members , a class’s data members cannot be initialized where they’re declared in the class body.
Objects contain only data, so objects are much smaller than if they also contained member functions. Applying operator sizeof to a class name or to an object of that class will report only the size of the class’s data members. The compiler creates one copy (only) of the member functions separate from all objects of the class. All objects of the class share this one copy.
Each object, of course, needs its own copy of the class’s data, because the data can vary among the objects. The function code is nonmodifiable and, hence, can be shared among all objects of one class.
Char
2013-04-25 11:41:01
1 char a,b,c; 2 a='a'; 3 b='\x61';//Hexadecimal. 4 c='\141';//Octonary. 5 printf("%d\ ",(unsigned int)a); 6 printf("%d\ ",(unsigned int)b); 7 printf("%d\ ",(unsigned int)c); 8 //output:97 97 97
1 #include <stdio.h> 2 3 int main() 4 { 5 unsigned char uc; 6 char c; 7 uc = 0x81; 8 c = uc; 9 printf("c=%d\n",c);//-127 10 printf("uc=%d\n",uc);//129 11 12 //sign extension with '1' 13 printf("%x\n",(unsigned int)c);//ffffff81 14 //sign extension with '0' 15 printf("%x\n",(unsigned int)uc);//81 16 //implicit type conversion 17 printf("%x\n",(unsigned char)c);//81 18 19 }
Treated as different types
"arm-linux-gcc" treats type char as "unsigned char" and "gcc" treats it as "signed char",so the below syntax blocks will get the same result.
1 unsigned char DecodeProtocol(unsigned char *pu8DataRx, unsigned char *pu8Data, unsigned char *pu8Length); 2 //'signed char'='unsigned char' when using 'x86 gcc'. 3 //'unsigned char'='unsigned char' when using 'arm-linux-gcc'. 4 char u8RetValue = DecodeProtocol(msgBuffer,au8RxDataToRead, &u8Length);
Pointer
There are three values that can be used to initialize a pointer: 0, NULL(but in C++, 0 is used by convention) or an address of an object of the same type. The new C++ standard also provides the nullptr constant, which is preferred.The only integer that can be assigned to a pointer without casting is zero.The value 0 is the only integer value that can be assigned directly to a pointer variable without first casting the integer to a pointer type.
If a value does not (or should not) change in the body of a function to which it’s passed,the parameter should be declared const.By default, objects are passed by value—a copy of the entire object is passed. This requires the execution-time overhead of making a copy of each data item in the object and storing it on the function call stack. When a pointer to an object is passed, only a copy of the address of the object must be made—the object itself is not copied.
Type of Void
All pointer types can be assigned to a pointer of type void * without casting. However, a pointer of type void * cannot be assigned directly to a pointer of another type—the pointer of type void * must first be cast to the proper pointer type.
So,in a function parameter,it is helpful to declare any pointer as type void.such as:
bool readFile(const char * _fileName,void * _pBuf,int _bufLen);
Then we can call this function by passing any type of pointer to it without casting.
char buf[32]; readFile(fileName,buf,sizeof(buf));
String literals
String literals have static storage class (they exist for the duration of the program) and may or may not be shared if the same string literal is referenced from multiple locations in a program. The effect of modifying a string literal is undefined; thus, you should always declare a pointer to a string literal as const char *.
1 char aChar[]="blue"; 2 //this statement has the same effect with the above statement. 3 char aChar[]={'b','l','u','e','\0'}; 4 //the effect of modifying a string literal is undefined, 5 //so you should always declare a pointer to string literal as 'const char *'. 6 const char * csPtr="blue";
Command-line Arguments
String arrays are commonly used with command-line arguments that are passed to function main when a program begins execution.Each pointer points to the first character of its corresponding string.
1 const char * const suit[4]={"Hearts", "Diamonds", "Clubs", "Spades"}; 2 int main(int argc,char * argv[]); 3 int main(int argc,char ** argv);
Selection Sort:
1 #include <iostream> 2 using namespace std; 3 4 //the first parameter can alse be declared as 'int array[]' 5 //the type of a array name is 'a const pointer to nonconstant type' 6 void selectionSort(int * const array,const int len); 7 void swap(int * const a,int * const b); 8 void printArray(int * const array,const int len); 9 10 int main() 11 { 12 const int size=10; 13 int aInt10[size]={12,32,15,6,54,73,32,75,5,50}; 14 printArray(aInt10,size); 15 selectionSort(aInt10,size); 16 printArray(aInt10,size); 17 } 18 19 void selectionSort(int * const array,const int len) 20 { 21 int smallest; 22 23 for(int i=0;i<len-1;++i) 24 { 25 smallest=i; 26 for(int index=i+1;index<len;++index) 27 { 28 if(array[index]<array[smallest]) 29 { 30 smallest=index; 31 } 32 } 33 34 swap(&array[i],&array[smallest]); 35 } 36 } 37 38 void swap(int * const a,int * const b) 39 { 40 int tmp; 41 tmp=*a; 42 *a=*b; 43 *b=tmp; 44 } 45 46 void printArray(int * const array,const int len) 47 { 48 for(int index=0;index<len;++index) 49 { 50 cout<<array[index]<<"\t"; 51 } 52 cout<<endl; 53 }
Array & Vector
Array
Arrays, structures and classes are “static” entities in that they remain the same size throughout program execution.The ISO/IEC C++ standard defines an “object” as any “region of storage.” Like objects of classes, fundamental-type variables also occupy space in memory, so they’re often referred to as “objects.”
1 //use initializer list to initialize array n 2 int n[10] = {21,42,53,74,23,64,75,97,23,43}; 3 4 //If there are fewer initializers than array elements,the 5 //remaining array elements are initialized to zero. 6 int n[10] ={}; 7 8 //If the array size is omitted from a declaration with an initializer list 9 //the compiler sizes the array to the number of elements in the initializer list. 10 int n[]={1,2,3,4,5}; 11 12 //constant variable can be used to specify array size 13 const int arraySize=10; 14 int s[arraySize]={}; 15 16 void func(void) 17 { 18 //if a static array is not initialized explicitly 19 //when they are declared,each element of that array 20 //is initialized to zero by the complier.But auto arrary 21 //is not. 22 static int staticArray[3]; 23 int autoArray[3]; 24 } 25 26 void constArgumentArray(const int b[]) 27 { 28 //array 'b' can not be modified within this 29 //function body when argument 'b' has a qualifier 'const' 30 } 31 32 int array1[2][3]={{1,2,3},{4,5,6}}; 33 int array2[2][3]={1,2,3,4,5}; 34 int array3[2][3]={{1,2},{2}}; 35 36 //determine the number of elements in an array. 37 double aReal[10]; 38 int nElements=sizeof(aReal)/sizeof(aReal[0]);
1 #include <iostream> 2 3 void insertionSort(int aInt[],int aIntSize); 4 void printArray(int aInt[],int aIntSize); 5 6 int main() 7 { 8 const int aIntSize=10; 9 int aInt[10]={23,43,56,87,89,56,12,56,64,71}; 10 printArray(aInt,10); 11 insertionSort(aInt,10); 12 printArray(aInt,10); 13 } 14 15 void insertionSort(int aInt[],int aIntSize) 16 { 17 int insert; 18 int moveItem; 19 20 for(int i=1;i<aIntSize;++i) 21 { 22 insert=aInt[i]; 23 moveItem=i; 24 25 while(0<moveItem && aInt[moveItem-1]>insert) 26 { 27 //need to be attention here 28 aInt[moveItem]=aInt[moveItem-1]; 29 --moveItem; 30 } 31 aInt[moveItem]= insert; 32 } 33 } 34 35 void printArray(int aInt[],int aIntSize) 36 { 37 for(int i=0;i<aIntSize;++i) 38 { 39 std::cout<<aInt[i]<<"\t"; 40 } 41 std::cout<<std::endl; 42 }
When a function receives a one-dimensional array as an argument, the array brackets are empty in the function’s parameter list. The size of a two-dimensional array’s first dimension (i.e., the number of rows) is not required either, but all subsequent dimension sizes are required. The compiler uses these sizes to determine the locations in memory of elements in multidimensional arrays. All array elements are stored consecutively in memory, regardless of the number of dimensions. In a two-dimensional array,row 0 is stored in memory followed by row 1. Each row is a one-dimensional array. To locate an element in a particular row, the function must know exactly how many elements are in each row so it can skip the proper number of memory locations when accessing the array. Thus, when accessing a[1][2], the function knows to skip row 0’s three elements in memory to get to row 1. Then, the function accesses element 2 of that row.
1 #include <iostream> 2 3 void printDimenIntArray(int aIntDimen[][3]); 4 5 int main() 6 { 7 int aIntDimensinal[][3]={{1,2,3},{4,5,6}}; 8 printDimenIntArray(aIntDimensinal); 9 } 10 11 void printDimenIntArray(int aIntDimen[][3]) 12 { 13 for(int i=0;i<2;i++) 14 { 15 for(int j=0;j<3;j++) 16 { 17 std::cout<<aIntDimen[i][j]<<"\t"; 18 } 19 std::cout<<std::endl; 20 } 21 }
Vector
Introduce to Vector
Function
Evaluation Order of Function Arguments
The order of evaluation of a function’s arguments,however, is not specified by the C++ standard. Thus, different compilers can evaluate function arguments in different orders. The C++ standard does guarantee that all arguments in a function call are evaluated before the called function executes.If you have doubts about the order of evaluation of a function’s arguments and whether the order would affect the values passed to the function, evaluate the arguments in separate assignment statements before the function call, assign the result of each expression to a local variable, then pass those variables as arguments to the function.
Return Control
There are three ways to return control to the point at which a function was invoked.If the function does not return a result (i.e., it has a void return type), control returns when the program reaches the function-ending right brace, or by execution of the statement "return;"If the function does return a result, the statement "return expression;"evaluates expression and returns the value of expression to the caller.
Argument Coercion
Sometimes, argument values that do not correspond precisely to the parameter types in the function prototype can be converted by the compiler to the proper type before the function is called.Converting values to lower fundamental types can result in incorrect values. Therefore,a value can be converted to a lower fundamental type only by explicitly assigning the
value to a variable of lower type (some compilers will issue a warning in this case) or by using a cast operator.
Below lists the fundamental data type from "highest type" to "lowest type".
Standard Library Headers
Header names ending in.h are “old-style” headers that have been superseded by the C++ Standard Library headers.
Some common C++ Standard Library headers are listed below:
Case Study:Game of Chance
1 #include <iostream> 2 //contain prototype for function 'rand()' &'srand()' 3 #include <cstdlib> 4 //contain prototype for function 'time()' 5 #include <ctime> 6 7 using namespace std; 8 9 int rollDice(); 10 11 int main() 12 { 13 //return the current time as the number of seconds since January 1,1970 at midnight GMT. 14 //this is converted to an unsigned int which is used as the seed of the random number generator. 15 srand(time(0)); 16 17 //declare a user-defined type called enumeration 18 //capitalize the first letter of an identifier used as a user-defined type name. 19 //using only upperletters in enumeration constant name can remind you that 20 //enumeration constants are not variable. 21 enum FinalStatus{WIN,LOSS,CONTINUE}; 22 23 FinalStatus status; 24 int sumOfDice; 25 int myPoint; 26 27 sumOfDice=rollDice(); 28 switch (sumOfDice) 29 { 30 case 7: 31 case 11: 32 status=WIN; 33 break; 34 case 2: 35 case 3: 36 case 12: 37 status=LOSS; 38 break; 39 default: 40 status=CONTINUE; 41 myPoint=sumOfDice; 42 break; 43 } 44 45 while(status==CONTINUE) 46 { 47 sumOfDice=rollDice(); 48 if(myPoint==sumOfDice) 49 status=WIN; 50 else if(7==sumOfDice) 51 status=LOSS; 52 } 53 54 if(WIN==status) 55 cout<<"you win the game"<<endl; 56 else 57 cout<<"you loss the game"<<endl; 58 59 } 60 61 int rollDice() 62 { 63 //rand() generate an unsigned int between 0 to RAND_MAX(a symbolic constant 64 //definded in <cstdlib> header. 65 //function rand() actually generates pseudorandom numbers which repeats itself 66 //each time the program executes. 67 int die1=1+rand()%6; 68 69 int die2=1+rand()%6; 70 cout<<"roll result:"<<die1<<"+"<<die2<<"="<<die1+die2<<endl; 71 return die1+die2; 72 }
Inline Function
If a member function is defined in the body of a class definition, the member function is implicitly declared inline. Remember that the compiler reserves the right not to inline any function. Reusable inline functions are typically placed in headers, so that their definitions can be included in each source file that uses them.Placing the qualifier inline before a function’s return type in the function definition “advises” the compiler to generate a copy of the function’s body code in place (when appropriate) to avoid a function call.
1 #include <iostream> 2 inline double cube(double a); 3 4 int main() 5 { 6 double x=5.1; 7 std::cout<<"x^3="<<cube(x)<<std::endl; 8 9 } 10 11 inline double cube(double a) 12 { 13 return a*a*a; 14 }
References and Reference Parameters
For passing large objects, use a constant reference parameter to simulate the appearance and security of pass-by-value and avoid the overhead of passing a copy of the large object.
1 #include <iostream> 2 3 using namespace std; 4 5 //A reference parameter is an alias for its coresponding argument in the function call. 6 void square(int& intRef); 7 8 int main() 9 { 10 int x=4; 11 cout<<"Before func call:"<<"x="<<x<<endl; 12 square(x); 13 cout<<"After func call:"<<"x="<<x<<endl; 14 15 int y=3; 16 //Reference variables must be initialized in their declarations. 17 //and can not be reassigned as aliases to other variables. 18 int& z=y; 19 cout<<"z="<<z<<endl; 20 } 21 22 void square(int& intRef) 23 { 24 intRef *=intRef; 25 } 26 27 //output: 28 //Before func call:x=4 29 //After func call:x=16 30 //z=3
Default Argument
1-When a program omits an argument for a parameter with a default argument in a function call, the compiler rewrites the function call and inserts the default value of that argument.
2-Default arguments must be the rightmost (trailing) arguments in a function’s parameter list.
3-Default arguments must be specified with the first occurrence of the function name—typically, in the function prototype,or might in the function header.
4-Default values can be any expression, including constants, global variables or function calls.
5-Default arguments also can be used with inline functions.
6-Any arguments passed to the function explicitly are assigned to the function’s parameters from left to right.
1 #include <iostream> 2 3 int volumn(int high=1,int width=1,int length=1); 4 5 int main() 6 { 7 int h=10,w=10,l=10; 8 std::cout<<"volumn(h):"<<volumn(h)<<std::endl; 9 std::cout<<"volumn(h,w):"<<volumn(h,w)<<std::endl; 10 std::cout<<"volumn(h,w,l):"<<volumn(h,w,l)<<std::endl; 11 } 12 13 int volumn(int high,int width,int length) 14 { 15 return high*width*length; 16 }
Unary Scope Operator
C++ provides the unary scope resolution operator (::) to access a global variable when a local variable of the same name is in scope.
Using the unary scope resolution operator (::) with a given variable name is optional when the only variable with that name is a global variable.
1 #include <iostream> 2 3 void read(); 4 int scope=10; 5 6 int main() 7 { 8 int scope=11; 9 //this two function call has the same effect. 10 read(); 11 ::read(); 12 13 std::cout<<scope<<std::endl;//11 14 std::cout<<::scope<<std::endl;//10 15 } 16 17 void read() 18 { 19 std::cout<<"I'm in grobal:read"<<std::endl; 20 }
Overload Function
C++ enables several functions of the same name to be defined, as long as they have different signatures.A signature is a combination of a function’s name and its parameter types (in order). The compiler uses only the parameter lists to distinguish between overloaded functions. Creating overloaded functions with identical parameter lists and different return types is a compilation error.Member functions of a class can be overloaded, but only by other member functions of that class.
The compiler encodes each function identifier with the number and types of its parameters (sometimes referred to as name mangling or name decoration) to enable type-safe linkage.Function-name mangling is compiler specific.Below show the mangled function names produced in assembly language by GNU C++. Each mangled name (other than main) begins with two underscores (__) followed by the letter Z, a number and the function name. The number that follows Z specifies how many characters are in the function’s name.The function name is then followed by an encoding of its parameter list The return types of the functions are not specified in the mangled names.
1 int square(int x){return x*x;} 2 3 double square(double y){return y*y;} 4 5 void nothing1( int a, float b, char c, int &d ){} 6 7 int nothing2( char a, int b, float &c, double &d ){return 0;} 8 9 int main() 10 { 11 12 } 13 14 //g++ -S overload.cpp -o overload.s 15 //_Z6squarei 16 //_Z6squared 17 //_Z8nothing1ifcRi 18 //_Z8nothing2ciRfRd
Function Template
You write a single function template definition. Given the argument types provided in calls to this function, C++ automatically generates separate function template specializations to handle each type of call appropriately.
1 #include <iostream> 2 3 //The formal type parameters are placeholders for fundamental types or user-defined 4 template<typename T> 5 T findMax(T a,T b,T c) 6 { 7 T max; 8 max = a; 9 if(b>max) 10 max=b; 11 if(c>max) 12 max=c; 13 return max; 14 } 15 16 int main() 17 { 18 int a=1,b=2,c=3; 19 double d=1.1,e=1.2,f=1.3; 20 char cha='a',chb='b',chc='c'; 21 22 std::cout<<"Max in 1,2,3 is:"<<findMax(a,b,c)<<std::endl; 23 std::cout<<"Max in 1.1,1.2,1.3 is:"<<findMax(d,e,f)<<std::endl; 24 std::cout<<"Max in a,b,c is:"<<findMax(cha,chb,chc)<<std::endl; 25 } 26 27 //Max in 1,2,3 is:3 28 //Max in 1.1,1.2,1.3 is:1.3 29 //Max in a,b,c is:c
Class
Class Scope
Variables declared in a member function have local scope and are known only to that function. If a member function defines a variable with the same name as a variable with class scope, the class-scope variable is hidden by the block-scope variable in the local scope.Such a hidden variable can be accessed by preceding the variable name with the class name followed by the scope resolution operator (::). Hidden global variables can be accessed with the scope resolution operators.
1 //scope.cpp 2 #include <iostream> 3 using namespace std; 4 5 int variable = 100; 6 7 void boo(); 8 class Foo 9 { 10 public: 11 static const int variable = 300; 12 }; 13 14 15 int main() 16 { 17 boo(); 18 } 19 20 void boo() 21 { 22 int variable = 200; 23 cout<<"grobal variable:"<<::variable<<endl; 24 cout<<"function variable:"<<variable<<endl; 25 cout<<"class variable:"<<Foo::variable<<endl; 26 27 }
Accessing Class Member
A class’s data members and member functions belong to that class’s scope. Nonmember functions are defined at global namespace scope.Outside a class’s scope, public class members are referenced through one of the handles on an object—an object name, a reference to an object or a pointer to an object.
1 // Demonstrating the class member access operators . and -> 2 #include <iostream> 3 using std::cout; 4 using std::endl; 5 6 // class Count definition 7 class Count 8 { 9 public: // public data is dangerous 10 // sets the value of private data member x 11 void setX( int value ) 12 { 13 x = value; 14 } // end function setX 15 16 // prints the value of private data member x 17 void print() 18 { 19 cout << x << endl; 20 } // end function print 21 22 private: 23 int x; 24 }; // end class Count 25 26 int main() 27 { 28 Count counter; // create counter object 29 Count *counterPtr = &counter; // create pointer to counter 30 Count &counterRef = counter; // create reference to counter 31 32 cout << "Set x to 1 and print using the object's name: "; 33 counter.setX( 1 ); // set data member x to 1 34 counter.print(); // call member function print 35 36 cout << "Set x to 2 and print using a reference to an object: "; 37 counterRef.setX( 2 ); // set data member x to 2 38 counterRef.print(); // call member function print 39 40 cout << "Set x to 3 and print using a pointer to an object: "; 41 counterPtr->setX( 3 ); // set data member x to 3 42 counterPtr->print(); // call member function print 43 return 0; 44 } // end main
Access Functions and Utility function
A utility function is not part of a class’s public interface; rather, it’s a private member function that supports the operation of the class’s other member functions. Utility functions are not intended to be used by clients of a class
1 #ifndef SALESP_H 2 #define SALESP_H 3 4 class SalesPerson 5 { 6 public: 7 SalesPerson(); // constructor 8 void getSalesFromUser(); // input sales from keyboard 9 void setSales( int, double ); // set sales for a specific month 10 void printAnnualSales(); // summarize and print sales 11 private: 12 double totalAnnualSales(); // prototype for utility function 13 double sales[ 12 ]; // 12 monthly sales figures 14 }; // end class SalesPerson 15 16 #endif
1 // Member functions for class SalesPerson. 2 #include <iostream> 3 using std::cout; 4 using std::cin; 5 using std::endl; 6 using std::fixed; 7 8 #include <iomanip> 9 using std::setprecision; 10 11 #include "SalesPerson.h" // include SalesPerson class definition 12 13 // initialize elements of array sales to 0.0 14 SalesPerson::SalesPerson() 15 { 16 for ( int i = 0; i < 12; i++ ) 17 sales[ i ] = 0.0; 18 } // end SalesPerson constructor 19 20 // get 12 sales figures from the user at the keyboard 21 void SalesPerson::getSalesFromUser() 22 { 23 double salesFigure; 24 25 for ( int i = 1; i <= 12; i++ ) 26 { 27 cout << "Enter sales amount for month " << i << ": "; 28 cin >> salesFigure; 29 setSales( i, salesFigure ); 30 } // end for 31 } // end function getSalesFromUser 32 33 // set one of the 12 monthly sales figures; function subtracts 34 // one from month value for proper subscript in sales array 35 void SalesPerson::setSales( int month, double amount ) 36 { 37 // test for valid month and amount values 38 if ( month >= 1 && month <= 12 && amount > 0 ) 39 sales[ month - 1 ] = amount; // adjust for subscripts 0-11 40 else // invalid month or amount value 41 cout << "Invalid month or sales figure" << endl; 42 } // end function setSales 43 44 // print total annual sales (with the help of utility function) 45 void SalesPerson::printAnnualSales() 46 { 47 cout << setprecision( 2 ) << fixed 48 << "\nThe total annual sales are: $" 49 << totalAnnualSales() << endl; // call utility function 50 } // end function printAnnualSales 51 52 // private utility function to total annual sales 53 double SalesPerson::totalAnnualSales() 54 { 55 double total = 0.0; // initialize total 56 57 for ( int i = 0; i < 12; i++ ) // summarize sales results 58 total += sales[ i ]; // add month i sales to total 59 60 return total; 61 } // end function totalAnnualSales
1 #include "SalesPerson.h" 2 3 int main() 4 { 5 SalesPerson s; // create SalesPerson object s 6 7 s.getSalesFromUser(); // note simple sequential code; 8 s.printAnnualSales(); // no control statements in main 9 return 0; 10 } // end main
Constructor
2013-03-23 00:52:31
In C++, the code that runs when an object is declared is called the constructor.Constructor is part of the public section of the class.If the constructor were not public, then no instances of the object could be created.
Const field initialization
If you declare a field of your class as const, then that field must be initialized in the initialization list:
The default arguments to the constructor ensure that, even if no values are provided in a constructor call, the constructor still initializes the data members. A constructor that defaults all its arguments is also a default constructor—that is, a constructor that can be invoked with no arguments. There can be at most one default constructor per class.
1 //ConstructorWithDefaultArgument.h 2 #ifndef __ARGUMENT_H__ 3 #define __ARGUMENT_H__ 4 5 class Time{ 6 public: 7 Time(int =12,int =0,int =0); 8 void setTime(int h,int m,int s); 9 int getTime(); 10 private: 11 int hour; 12 int minute; 13 int second; 14 15 }; 16 #endif 17 18 //---------------------------------------------------------------------- 19 //ConstructorWithDefaultArgument.cpp 20 #include <iostream> 21 #include "ConstructorWithDefaultArgument.h" 22 using namespace std; 23 24 Time::Time(int h,int m,int s) 25 { 26 setTime(h,m,s); 27 } 28 29 void Time::setTime(int h,int m,int s) 30 { 31 hour = h; 32 minute = m; 33 second = s; 34 } 35 36 int Time::getTime() 37 { 38 int t; 39 t = hour *10000+minute*100+second; 40 cout<<"Here is the time: "<<t<<endl; 41 } 42 43 #include "ConstructorWithDefaultArgument.h" 44 45 //---------------------------------------------------------------------- 46 //main.cpp 47 int main() 48 { 49 Time myTime1; 50 Time myTime2(13); 51 Time myTime3(13,14); 52 Time myTime4(13,14,15); 53 54 myTime1.getTime(); 55 myTime2.getTime(); 56 myTime3.getTime(); 57 myTime4.getTime(); 58 }
Having the Time constructor call setTime enables us to limit the changes to code that validates the hour, minute or second to the corresponding set function. This reduces the likelihood of errors when altering the class’s implementation.
If a member function of a class already provides all or part of the functionality required by a constructor (or other member function) of the class, call that member function from the constructor (or other member function). This simplifies the maintenance of the code and reduces the likelihood of an error if the implementation of the code is modified. As a general rule: Avoid repeating code.
When Constructor and Destructor are called
The order in which these function calls occur depends on the order in which execution enters and leaves the scopes where the objects are instantiated.Generally, destructor calls are made in the reverse order of the corresponding constructor calls.
A class’s destructor is called implicitly when an object is no longer needs. So what does it actually mean for an object to be "no longer needed"? It means one of three things:
1) When you delete a pointer to an object
2) When the object goes out of scope
3) When the object belongs to a class whose destructor is being called
This occurs, for example, as an automatic object is destroyed when program execution leaves the scope in which that object was instantiated. The destructor itself does not actually release the object’s.memory—it performs termination housekeeping before the object’s memory is reclaimed, so the memory may be reused to hold new objects.If you do not explicitly provide a destructor, the compiler creates an “empty” destructor.It’s a syntax error to attempt to pass arguments to a destructor, to specify a return type for a destructor (even void cannot be specified), to return values from a destructor or to overload a destructor.
1 //Destructor.h 2 #ifndef __DESTRUCTOR_H__ 3 #define __DESTRUCTOR_H__ 4 5 class Destructor 6 { 7 public: 8 Destructor(int n); 9 ~Destructor(); 10 private: 11 int number; 12 }; 13 14 15 #endif 16 17 //-------------------------------------------------------------------------- 18 //Destructor.cpp 19 #include <iostream> 20 #include "Destructor.h" 21 22 using namespace std; 23 24 Destructor::Destructor(int n) 25 { 26 number = n; 27 cout<<"Constructor: object "<<number<<endl; 28 } 29 30 Destructor::~Destructor() 31 { 32 cout<<"Destructor: object "<<number<<endl; 33 } 34 35 36 //-------------------------------------------------------------------------- 37 //main.cpp 38 #include "Destructor.h" 39 40 Destructor myDestructor1(1); 41 void create(); 42 43 int main() 44 { 45 static Destructor myDestructor2(2); 46 Destructor myDestructor3(3); 47 create(); 48 Destructor myDestructor4(4); 49 } 50 51 void create() 52 { 53 Destructor myDestructor5(5); 54 static Destructor myDestructor6(6); 55 Destructor myDestructor7(7); 56 }
Default Memberwise Assignment
2013-03-23 11:40:52
The assignment operator (=) can be used to assign an object to another object of the same type. By default, such assignment is performed by memberwise assignment—each data member of the object on the right of the assignment operator is assigned individually to the same data member in the object on the left of the assignment operator.
Caution: Memberwise assignment can cause serious problems when used with a class whose data members contain pointers to dynamically allocated memory.
Objects may be passed as function arguments and may be returned from functions. Such passing and returning is performed using pass-by-value by default—a copy of the object is passed or returned. In such cases, C++ creates a new object and uses a copy constructor to copy the original object’s values into the new object. For each class, the compiler provides a default copy constructor that copies each member of the original object nto the corresponding member of the new object.
Passing an object by value is good from a security standpoint, because the called function has no access to the original object in the caller, but pass-by-value can degrade performance when making a copy of a large object. An object can be passed by reference by passing either a pointer or a reference to the object. Pass-by-reference offers good performance but is weaker from a security standpoint, because the called function is given access to the original object. Pass-by-const-reference is a safe, good-performing alternative (this can be implemented with a const reference parameter or with a pointer-to-const-data parameter).
1 //DefaultMemberwise.h 2 #ifndef __DEFAULT_MEMBERWISE_H__ 3 #define __DEFAULT_MEMBERWISE_H__ 4 5 class Memberwise 6 { 7 public: 8 Memberwise(int y=2013,int m=3,int d=23); 9 void getDate(); 10 private: 11 int year; 12 int month; 13 int date; 14 }; 15 16 #endif 17 18 //-------------------------------------------------------------------------- 19 //DefaultMemberwise.cpp 20 #include "DefaultMemberwise.h" 21 #include <iostream> 22 using namespace std; 23 24 Memberwise::Memberwise(int y,int m,int d):year(y),month(m),date(d) 25 { 26 27 } 28 29 void Memberwise::getDate() 30 { 31 cout<<"date:"<< year*10000+month*100+date<<endl; 32 } 33 34 //------------------------------------------------------------------------- 35 //main.cpp 36 #include "DefaultMemberwise.h" 37 38 int main() 39 { 40 Memberwise myMemberwise1(2012,12,12); 41 Memberwise myMemberwise2; 42 43 myMemberwise1.getDate(); 44 myMemberwise2.getDate(); 45 46 myMemberwise2=myMemberwise1; 47 myMemberwise2.getDate(); 48 }
Const Object and Const Member Function
2013-03-23 20:25:27
You may use keyword const to specify that an object is not modifiable and that any attempt to modify the object should result in a compilation error.Declaring variables and objects const when appropriate can improve performance—compilers can perform optimizations on constants that cannot be performed on variables.
C++ disallows member function calls for const objects unless the member functions themselves are also declared const. Invoking a non-const member function on a const object is a compilation error.
A member function is specified as const both in its prototype by inserting the keyword const after the function’s parameter list and, in the case of the function definition, before the left brace that begins the function body.The fact that a member function does not modify an object is not sufficient to indicate that the function is a constant function—the function must explicitly be declared const.A const member function can't modify a data member or call a none-const member function.But a const member function can be overloaded with a non-const version. The compiler chooses which overloaded member function to use based on the object on which the function is invoked. If the object is const, the compiler uses the const version. If the object is not const, the compiler uses the non-const version.
Attempting to declare a constructor or destructor const is a compilation error.A constructor must be a non-const member function , but it can still be used to initialize a const object.Invoking a nonconst member function from the constructor call as part of the initialization of a const object is allowed. The “constness” of a const object is enforced from the time the constructor completes initialization of the object until that object’s destructor is called.
All data members can be initialized using member initializer syntax, but const data members and data members that are references must be initialized using member initializers. We’ll also see that member objects must be initialized this way as well.
Declare as const all of a class’s member functions that do not modify the object in which they operate.Declaring such member functions const does offer a benefit,. If the member function is inadvertently written to modify the object, the compiler will issue an error message.
1 //const.h 2 #ifndef __CONST_H__ 3 #define __CONST_H__ 4 5 class Book 6 { 7 public: 8 Book(int e,int m,int p); 9 void setData(int e); 10 //Declare as const all if it do not modify the object 11 void getDate() const; 12 void displayDate(); 13 14 private: 15 int english; 16 //warning ,should better not unless it is static member 17 const int math =96; 18 const int physics; 19 20 }; 21 22 #endif 23 24 //-------------------------------------------------------------------------- 25 //const.cpp 26 #include <iostream> 27 #include "Const.h" 28 using namespace std; 29 30 Book::Book(int e,int m,int p):physics(p) 31 { 32 //Invoking a nonconst member function from the constructor 33 setData(e); 34 } 35 36 void Book::setData(int e) 37 { 38 english =e; 39 } 40 41 void Book::getDate()const 42 { 43 cout<<"english: "<<english<<endl; 44 cout<<"math: "<<math<<endl; 45 cout<<"physics "<<physics<<endl; 46 47 } 48 49 void Book::displayDate() 50 { 51 cout<<"english: "<<english<<endl; 52 cout<<"math: "<<math<<endl; 53 cout<<"physics "<<physics<<endl; 54 } 55 56 //------------------------------------------------------------------------- 57 //main.cpp 58 #include "Const.h" 59 60 int main() 61 { 62 const Book myBook1(93,94,95); 63 Book myBook2(83,84,85); 64 65 myBook2.getDate();//ok 66 myBook2.displayDate();//ok 67 myBook1.getDate();//ok 68 //error, displayData() is not const member function 69 //myBook1.displayDate(); 70 71 }
Composition
2013-03-24 10:04:23
Member objects are constructed in the order in which they’re declared in the class definition (not in the order they’re listed in the constructor’s member initializer list) and before their enclosing class objects are constructed.To confirm in the program output we see that objects are constructed from the inside out and destroyed in the reverse order, from the outside in.That is the Employee destructor runs first then the member objects are destructed in the reverse order from which they were constructed. (i.e., the Date member objects are destroyed after the Employee object that contains them).
The compiler provides each class with a default copy constructor that copies each data member of the constructor’s argument object into the corresponding member of the object being initialized.When each of the Employee’s Date member object’s is initialized in the Employee constructor’s member initializer list , the default copy constructor for class Date is called. Since this constructor is defined implicitly by the compiler, it does not contain any output statements to demonstrate when it’s called.So we can't see any output from two objects' constructor of Date.If a member object is not initialized through a member initializer, the member object’s default constructor will be called implicitly.So if we change the Employee's constructor to the form of none initialize the object members explicity,we will see the output form the Date constructor.A compilation error occurs if a member object is not initialized with a member initializer and the member object’s class does not provide a default constructor.
1 //------------------------------------------------------------------------- 2 //composition_date.h 3 #ifndef __COM_DATE_H__ 4 #define __COM_DATE_H__ 5 using namespace std; 6 7 class Date 8 { 9 public: 10 Date(int year=2012,int month=12,int day=12); 11 ~Date(); 12 //void setData(int year,int month,int day); 13 //void getData() const; 14 private: 15 int year; 16 int month; 17 int day; 18 }; 19 20 21 22 #endif 23 24 25 //------------------------------------------------------------------------- 26 //composition_date.cpp 27 #include <iostream> 28 #include "composition_date.h" 29 using namespace std; 30 31 Date::Date(int year, int month ,int day) 32 :year(year),month(month),day(day) 33 { 34 cout<<"Date Constructor: "<<year<<"/"<<month<<"/"<<day<<endl; 35 } 36 37 Date::~Date() 38 { 39 40 cout<<"Date Destructor: "<<year<<"/"<<month<<"/"<<day<<endl; 41 } 42 43 //------------------------------------------------------------------------- 44 //composition_employee.h 45 #include "composition_date.h" 46 #include <iostream> 47 using namespace std; 48 49 class Employee 50 { 51 public: 52 Employee(string,string,const Date &); 53 ~Employee(); 54 private: 55 string firstName; 56 string lastName; 57 Date birthDate; 58 Date employDate; 59 }; 60 61 62 //------------------------------------------------------------------------- 63 //composition_employee.cpp 64 #include "composition_date.h" 65 #include "composition_employee.h" 66 #include <iostream> 67 using namespace std; 68 69 Employee::Employee(string firstN,string lastN,const Date & birthD) 70 :firstName(firstN),lastName(lastN),birthDate(birthD) 71 { 72 cout<<"employee contructor"<<endl; 73 } 74 75 Employee::~Employee() 76 { 77 cout<<"employee destructor"<<endl; 78 } 79 80 81 //------------------------------------------------------------------------- 82 //main.cpp 83 #include <iostream> 84 #include "composition_date.h" 85 #include "composition_employee.h" 86 using namespace std; 87 88 int main() 89 { 90 Date bD(2012,1,1); 91 Date eD(2013,3,3); 92 cout<<endl; 93 94 //There are actually five constructor call. 95 Employee myEmployee("benson","huang",bD); 96 97 cout<<endl; 98 cout<<"main exit..."<<endl; 99 }
Friend
2013-03-25 13:12:34
A friend function of a class is defined outside that class’s scope, yet has the right to access the non-public (and public)members of the class.Standalone functions, entire classes or member functions of other classes may be declared to be friends of another class.
To declare a function as a friend of a class,precede the function prototype in the class definition with keyword friend.To declare all member functions of class ClassTwo as friends of class ClassOne,place a declaration of the form in the definition of class ClassOne.
friend class ClassTwo;
Member access notions of private, protected and public are not relevant to friend declarations,so friend declarations can be placed anywhere in a class definition. Place notice that it is better to all friendship declarations first inside the classdefinition’s body and do not precede them with any access specifier.
Friendship is granted, not taken. Also, the friendship relation is neither symmetric nor transitive;i.e., if class A is a friend of class B,and class B is a friend of class C, you cannot infer that class B is a friend of class A , that class C is a friend of class B , or that class A is a friend of class C .
1 #include <iostream> 2 using namespace std; 3 4 class Year 5 { 6 friend void setYear(Year &,int); 7 public: 8 int getYear(){return year;} 9 private: 10 int year; 11 }; 12 13 void setYear(Year & oYear,int y); 14 15 int main() 16 { 17 Year oYear; 18 int thisYear = 2013; 19 cout<<"Before setting,this year is: "<<oYear.getYear()<<endl; 20 setYear(oYear,thisYear); 21 cout<<"After setting,this year is: "<<oYear.getYear()<<endl; 22 }
This
Every objecthas ac-cess to itsown address through apointercalled this (a C++keyword). The this pointer is not part of the object itself—i.e.,the memory occupiedbythe this pointerisnot re-flectedinthe result of a sizeof operation on theobject.The typeofthe this pointer depends on thetype of the object and whether the member function in which this is used is declared const.For example,in a nonconstant member function of class Employee,the this pointer hastype Employee *const (a constant pointer to a nonconstant Employee object). In aconstant member function of theclass Employee,the this pointerhas the data type const Employee *const (a constant pointer to a constant Employee object).Another use of the this pointer is to enable cascaded member-function calls—that is, invoking multiple functions in the same statement.
1 //this.h 2 #ifndef __THIS_H__ 3 #define __THIS_H__ 4 5 class Time 6 { 7 public: 8 Time(int=0,int=0,int=0); 9 Time & setTime(int,int,int); 10 void printTime()const; 11 private: 12 int hour; 13 int minute; 14 int second; 15 }; 16 17 #endif 18 19 20 //-------------------------------------------------------------------------- 21 //this.cp 22 #include <iostream> 23 #include <iomanip> 24 #include <stdexcept> 25 #include "this.h" 26 using namespace std; 27 28 Time::Time(int hour,int minute,int second) 29 { 30 setTime(hour,minute,second); 31 } 32 33 Time& Time::setTime(int h,int m,int s) 34 { 35 if(h>=0 && h <=24) 36 hour =h; 37 else 38 throw invalid_argument("hour:1~24"); 39 40 if(m>=0 && m<=59) 41 minute = m; 42 else 43 throw invalid_argument("minute:1~59"); 44 45 if(s>=0 && s<=59) 46 second = s; 47 else 48 throw invalid_argument("second:1~59"); 49 50 return *this; 51 } 52 53 void Time::printTime()const 54 { 55 cout<<"Time: "<<setfill('0')<<setw(2)<<( (hour ==0 || hour==12)?12:hour%12 )<<":" 56 <<setfill('0')<<setw(2)<<minute<<":"<<setfill('0')<<setw(2)<<second 57 <<" "<<(hour<12 ? "am":"pm")<<endl; 58 } 59 60 //-------------------------------------------------------------------------- 61 //main.cpp 62 63 #include "this.h" 64 65 int main() 66 { 67 Time time; 68 time.printTime(); 69 time.setTime(14,7,28).printTime(); 70 }
C File Processing
Data Hierarchy
The smallest data item in a computer can assume the value 0 or the value 1. Such a data item is called a bit.Just as characters are composed of bits, fields are composed of characters. A field is a group of characters that conveys meaning.A record (i.e., a struct in C) is composed of several fields.A group of related files is sometimes called a database. A collection of programs designed to create and manage databases is called a database management system (DBMS).
To facilitate the retrieval of specific records from a file, at least one field in each record is chosen as a record key. A record key identifies a record as belonging to a particular person or entity.There are many ways of organizing records in a file. The most popular type of organization is called a sequential file, in which records are typically stored in order by the
record key field.
Files and Streams
C views each file simply as a sequential stream of bytes . Each file ends either with an end-of-file marker or at a specific byte number recorded in a system-maintained,administrative data structure. When a file is opened, a stream is associated with the file.Three files and their associated streams are automatically opened when program execution begins—the standard input, the standard output and the standard error. Streams provide communication channels between files and programs.Opening a file returns a pointer to a FILE structure (defined in <stdio.h>) that contains information used to process the file. This structure includes a file descriptor, i.e., an index into an operating system array called the open file table. Each array element contains a file control block (FCB) that the operating system uses to administer a particular file. The standard input, standard output and standard error are manipulated using file pointers stdin, stdout and stderr.
1 //equivalent 2 fgetc(stdin); 3 getchar(); 4 5 //equivalent 6 fputc('a',stdout); 7 putchar('a');
New & Delete
The object or array is created in the free store (also called the heap)—a region of memory assigned to each program for storing dynamically allocated objects.
New
The new operator allocates storage of the proper size for an object of type Time, calls the default constructor to initialize the object and returns a pointer to the type specified to the right of the new operator (i.e., a Time *). If new is unable to find sufficient space in memory for the object, it indicates that an error occurred by throwing an exception.The keyword new is used to initialize pointers with memory form free store.
int * pInt = new int;
Time * poTime = new Time();
The code that uses pInt must eventually return this memory back to the free store,an operation called freeing the memory. To return the memory back to free store,you use the delete keyword.The delete operation frees up the memory allcoted through new.This statement first calls the destructor for the object to which timePtr points, then deallocates the memory associated with the object, returning the memory to the free store.
delete pInt; pInt=NULL;
delete poTime;
poTime=NULL;
You can dynamically allocate an array of memory using new and assign the memory to a pointer.The size of an array created at compile time must be specified using a constant integral expression; however, a dynamically allocated array’s size can be specified using any non-negative integral expression that can be evaluated at execution time. Also, when allocating an array of objects dynamically, you cannot pass arguments to each object’s constructor—each object is initialized by its default constructor. For fundamental types, the elements are initialized to 0 or the equivalent of 0 (e.g., chars are initialized to the null character, '\0').
int * paInt = new int[8];
If the pointer points to an array of objects, the statement first calls the destructor for every object in the array, then deallocates the memory. If the preceding statement did not include the
square brackets ([]) and gradesArray pointed to an array of objects, the result is undefined.
Some compilers call the destructor only for the first object in the array. Using delete on a null pointer (i.e., a pointer with the value 0) has no effect.
1 int gradeArray[] = new int[10]; 2 delete [] gradeArray;
Delete
Now you can use paInt just as if it pointed to an array.Unlink array,though,you need to free the memory pointed to by paInt,whereas ,you never want to free a pointer pointing to a statically declared array.The brackets tell the compiler that the pointer points to an array of values rather than a signal value.
delete[] paInt;
'delete' and 'delete[]' are different if the type of the array is 'class T',but the same with primitive type.
1 #include <iostream> 2 using namespace std; 3 4 class T 5 { 6 public: 7 T() 8 { 9 count++; 10 cout<<"constructor: "<<count<<endl; 11 12 } 13 ~T() 14 { 15 cout<<"destructor: "<<count<<endl; 16 --count; 17 } 18 private: 19 static int count; 20 }; 21 22 int T::count=0; 23 24 25 int main() 26 { 27 const int size = 3; 28 T * mInstance=new T[size]; 29 //only free the memory occupied by mInstance[0] 30 delete mInstance; 31 32 T * mInstance_1=new T[size]; 33 //free the memory occupied by mInstance_1[0] mInstance_1[1] mInstance_1[2] 34 delete[] mInstance_1; 35 }
Use Case
1 #include <iostream> 2 #include <cstring> 3 #include <stdio.h> 4 5 using namespace std; 6 7 int * growArray(int * pointer,int *pSize); 8 9 int main() 10 { 11 int size=3; 12 int * pointer=new int[size]; 13 memset(pointer,0,size*sizeof(int)); 14 int nextElement=0; 15 int val; 16 17 cout<<"Please enter integer number:\n"<<endl; 18 cin>>val; 19 20 while(0!=val) 21 { 22 //if the array's size is not enough,enlarge the array. 23 if(size==nextElement) 24 { 25 pointer=growArray(pointer,&size); 26 } 27 28 pointer[nextElement]=val; 29 ++nextElement; 30 cout<<"Please enter integer number(or 0 to exit):\n"<<endl; 31 cin>>val; 32 }//End of while. 33 34 //print out the int array after the user enter '0' 35 cout<<"the int array you enter is:"<<endl; 36 for(int i=0;0!=pointer[i];++i) 37 { 38 cout<<pointer[i]<<" "<<endl; 39 }//End of print int array. 40 41 }//End of function main. 42 43 int * growArray(int * pointer,int *pSize) 44 { 45 int size=*pSize; 46 int newSize=size*2; 47 *pSize *=2; 48 int * newPointer=new int[newSize]; 49 memset(newPointer,0,newSize*sizeof(int)); 50 51 for(int i=0;i<size;++i) 52 { 53 newPointer[i]=pointer[i]; 54 } 55 //'delete pointer' is also ok,because pointer points to an array of primitive type. 56 delete[] pointer; 57 return newPointer; 58 }
Inheritance & Polymorphism
Base classes and derived classes
With multiple inheritance, a derived class inherits from two or more (possibly unrelated) base classes.Each arrow in the hierarchy (Fig. 12.2) represents an is-a relationship.CommunityMember is the direct base class of Employee, Student and Alumnus. In addition, CommunityMember is an indirect base class of all the other classes in the diagram.
With all forms of inheritance, private members of a base class are not accessible directly from that class’s derived classes, but these private base-class members are still inherited.With public inheritance, all other base-class members retain their original member access when they become members of the derived class(e.g., public members of the base class become public members of the derived class, and,as we’ll soon see, protected members of the base class become protected members of the
derived class).
class Apple:public Shape { };
Relationship between SubClass and SuperClass
Derived-class member functions can refer to public and protected members of the base class simply by using the member names.When a derived-class member function redefines a base-class member function, the base-class member can still be accessed from the derived class by preceding the base-class member name with the base-class name and the scope resolution operator (::).
1 #include <iostream> 2 3 using namespace std; 4 5 class Book 6 { 7 public: 8 void cover(); 9 }; 10 11 void Book::cover() 12 { 13 cout<<"SuperClass:cover()"<<endl; 14 } 15 16 class Physical:public Book 17 { 18 public: 19 void cover(); 20 }; 21 22 void Physical::cover() 23 { 24 //base class member function can still be accessed. 25 Book::cover(); 26 cout<<"SubClass:cover()"<<endl; 27 } 28 29 int main() 30 { 31 Physical myPhysical; 32 //output:"SuperClass:cover()" 33 // "SubClass:cover()" 34 myPhysical.cover(); 35 }
C++ requires that a derived-class constructor call its base-class constructor to initialize the base-class data members that are inherited into the derived class.If Subclass’s constructor did not invoke Super Class's constructor explicitly, C++ would attempt to invoke SuperClass’s default constructor implicitly—but if the SuperClass does not have such a constructor, so the compiler would issue an error.
Constructor and Destructor in Derived Classes
When a program creates a derived-class object, the derived-class constructor immediately calls the base-class constructor, the base-class constructor’s body executes, then the derived class’s member initializers execute and finally the derived-class constructor’s body executes. This process cascades up the hierarchy if it contains more than two levels.
When a derived-class object’s destructor is called, the destructor performs its task, then invokes the destructor of the next base class up the hierarchy. This process repeats until the destructor of the final base class at the top of the hierarchy is called. Then the object is removed from memory.
#include <iostream> #include <string> using namespace std; class Book { public: //self-define constructor,and there is no default constructor in the class. Book(const string & strRef); ~Book(); }; Book::Book(const string & strRef) { cout<<"constructor Book:"<<strRef<<endl; } Book::~Book() { cout<<"Destructor Book"<<endl; } class Physical:public Book { public: Physical(const string & strRef); ~Physical(); }; //call the superclass's constructor in the initialization list. //if we do not call that constructor,we will get a compile which fail //to call Book::Book(); Physical::Physical(const string & strRef):Book(strRef) { cout<<"Constructor Physical"<<endl; } Physical::~Physical() { cout<<"Destructor Physical"<<endl; } int main() { Physical * myPhysical = new Physical("benson"); delete myPhysical; while(true); } //output:: //constructor Book:benson //Constructor Physical //Destructor Physical //Destructor Book
Public,Protected and Private Inheritance
When deriving a class from a base class, the base class may be inherited through public,protected or private inheritance.Figure 12.16 summarizes for each type of inheritance the accessibility of base-class members in a derived class.
Invoking Base-Class functions from Derived-Class
Invoking a function via the base-class pointer invokes the base-class functionality in the derived-class object—i.e., the type of the handle determines which function is called.
1 #include <iostream> 2 3 using namespace std; 4 5 class Book 6 { 7 public: 8 void cover(); 9 virtual void cost(); 10 }; 11 12 void Book::cover() 13 { 14 cout<<"SuperClass:cover()"<<endl; 15 } 16 17 void Book::cost() 18 { 19 cout<<"cost() in Book"<<endl; 20 } 21 22 class Physical:public Book 23 { 24 public: 25 void cover(); 26 virtual void cost(); 27 void page(); 28 }; 29 30 void Physical::cover() 31 { 32 cout<<"SubClass:cover()"<<endl; 33 } 34 35 void Physical::cost() 36 { 37 cout<<"cost() in Physical"<<endl; 38 } 39 40 void Physical::page() 41 { 42 cout<<"SubClass:page()"<<endl; 43 } 44 45 int main() 46 { 47 //Base-Class pointer point to Derived-Class. 48 Book * poBook=new Physical(); 49 poBook->cover();//SuperClass:cover() 50 51 //if a base-class pointer is aimed at a derived-class object, and an attempt is 52 //made to access a derived-class-only member function, a compilation error will occur. 53 poBook->page();//error 54 55 //if we explicitly cast the base-class pointer to 56 //a derived-class pointer—this is known as downcasting,we can 57 //access to derived-class-only members. 58 ((Physical *)poBook)->page(); 59 60 //Derived-Class pointer point to Base-Class will get an compiler error. 61 Physical * poPhysical = new Book();//error 62 }
Virtual Functions
From an implementation perspective, overriding a function is no different than redefining one. An overridden function in a derived class has the same signature and return type (i.e., prototype) as the function it overrides in its base class. If we do not declare the base-class function as virtual, we can redefine that function. By contrast, if we declare the base-class function as virtual, we can override that
function to enable polymorphic behavior.
If a program invokes a virtual function through a base-class pointer to a derivedclass object or a base-class reference to a derived-class object, the program will choose the correct derived-class draw function dynamically (i.e., at execution time) based on the object type—not the pointer or reference
type. Choosing the appropriate function to call at execution time (rather than at compile time) is known as dynamic binding or late binding.
int main() { //Base-Class Book's cost() function is override by the Derived-Class function poBook->cost();//output:'cost() ins Physical' //dynamic binding with virtual functions occurs only off //pointer as well as reference handles. Physical myPhysical; Book & bookRef=myPhysical; bookRef.cost();//output:'cost() ins Physical' }
Abstract Classes and Pure Virtual Functions
There are cases in which it’s useful to define classes from which you never intend to instantiate any objects. Such classes are called abstract classes. Because these classes normally are used as base classes in inheritance hierarchies, we refer to them as abstract base classes.These classes cannot be used to instantiate objects,classes that can be used to instantiate objects are called concrete classes.
A class is made abstract by declaring one or more of its virtual functions to be “pure.” A pure virtual function is specified by placing “= 0” in its declaration, Pure virtual functions do not provide implementations. Every concrete derived class must override all base-class pure virtual functions with concrete implementations of those functions.
virtual void draw() const = 0; // pure virtual function
The difference between a virtual function and a pure virtual function is that a virtual function has an implementation and gives the derived class the option of overriding the function; by contrast, a pure virtual function does not provide an implementation and requires the derived class to override the function for that derived class to be concrete; otherwise the derived class remains abstract
Simple Polymorphism
1 #include <iostream> 2 #include <vector> 3 4 using namespace std; 5 6 class Shape 7 { 8 public: 9 virtual void draw()=0; 10 }; 11 12 class Apple:public Shape 13 { 14 public: 15 virtual void draw() 16 { 17 cout<<"Draw Apple."<<endl; 18 } 19 }; 20 21 class Pear:public Shape 22 { 23 virtual void draw() 24 { 25 cout<<"Draw Pear."<<endl; 26 } 27 }; 28 29 int main() 30 { 31 vector<Shape *> SomeShape; 32 SomeShape.push_back(new Apple()); 33 SomeShape.push_back(new Pear()); 34 35 for(vector<Shape *>::iterator itr=SomeShape.begin(),end=SomeShape.end();itr!=end;++itr) 36 { 37 (*itr)->draw(); 38 } 39 }
Polymorphism,Virtual Functions and Dynamic Binding "under the hood"
1、The UML class diagram shows the inheritance hierarchy for our polymorphic employee payroll application.
2、Source Code
1 // Employee.h 2 // Employee abstract base class. 3 #ifndef EMPLOYEE_H 4 #define EMPLOYEE_H 5 6 #include <string> // C++ standard string class 7 using std::string; 8 9 class Employee 10 { 11 public: 12 Employee( const string &, const string &, const string & ); 13 14 void setFirstName( const string & ); // set first name 15 string getFirstName() const; // return first name 16 17 void setLastName( const string & ); // set last name 18 string getLastName() const; // return last name 19 20 void setSocialSecurityNumber( const string & ); // set SSN 21 string getSocialSecurityNumber() const; // return SSN 22 23 // pure virtual function makes Employee abstract base class 24 virtual double earnings() const = 0; // pure virtual 25 virtual void print() const; // virtual 26 private: 27 string firstName; 28 string lastName; 29 string socialSecurityNumber; 30 }; // end class Employee 31 32 #endif // EMPLOYEE_H
1 //Employee.cpp 2 // Abstract-base-class Employee member-function definitions. 3 // Note: No definitions are given for pure virtual functions. 4 #include <iostream> 5 using std::cout; 6 7 #include "Employee.h" // Employee class definition 8 9 // constructor 10 Employee::Employee( const string &first, const string &last, 11 const string &ssn ) 12 : firstName( first ), lastName( last ), socialSecurityNumber( ssn ) 13 { 14 // empty body 15 } // end Employee constructor 16 17 // set first name 18 void Employee::setFirstName( const string &first ) 19 { 20 firstName = first; 21 } // end function setFirstName 22 23 // return first name 24 string Employee::getFirstName() const 25 { 26 return firstName; 27 } // end function getFirstName 28 29 // set last name 30 void Employee::setLastName( const string &last ) 31 { 32 lastName = last; 33 } // end function setLastName 34 35 // return last name 36 string Employee::getLastName() const 37 { 38 return lastName; 39 } // end function getLastName 40 41 // set social security number 42 void Employee::setSocialSecurityNumber( const string &ssn ) 43 { 44 socialSecurityNumber = ssn; // should validate 45 } // end function setSocialSecurityNumber 46 47 // return social security number 48 string Employee::getSocialSecurityNumber() const 49 { 50 return socialSecurityNumber; 51 } // end function getSocialSecurityNumber 52 53 // print Employee's information (virtual, but not pure virtual) 54 void Employee::print() const 55 { 56 cout << getFirstName() << ' ' << getLastName() 57 << "\nsocial security number: " << getSocialSecurityNumber(); 58 } // end function print
1 //SalariedEmployee.h 2 // SalariedEmployee class derived from Employee. 3 #ifndef SALARIED_H 4 #define SALARIED_H 5 6 #include "Employee.h" // Employee class definition 7 8 class SalariedEmployee : public Employee 9 { 10 public: 11 SalariedEmployee( const string &, const string &, 12 const string &, double = 0.0 ); 13 14 void setWeeklySalary( double ); // set weekly salary 15 double getWeeklySalary() const; // return weekly salary 16 17 // keyword virtual signals intent to override 18 virtual double earnings() const; // calculate earnings 19 virtual void print() const; // print SalariedEmployee object 20 private: 21 double weeklySalary; // salary per week 22 }; // end class SalariedEmployee 23 24 #endif // SALARIED_H
1 //SalariedEmployee.cpp 2 // SalariedEmployee class member-function definitions. 3 #include <iostream> 4 using std::cout; 5 6 #include "SalariedEmployee.h" // SalariedEmployee class definition 7 8 // constructor 9 SalariedEmployee::SalariedEmployee( const string &first, 10 const string &last, const string &ssn, double salary ) 11 : Employee( first, last, ssn ) 12 { 13 setWeeklySalary( salary ); 14 } // end SalariedEmployee constructor 15 16 // set salary 17 void SalariedEmployee::setWeeklySalary( double salary ) 18 { 19 weeklySalary = ( salary < 0.0 ) ? 0.0 : salary; 20 } // end function setWeeklySalary 21 22 // return salary 23 double SalariedEmployee::getWeeklySalary() const 24 { 25 return weeklySalary; 26 } // end function getWeeklySalary 27 28 // calculate earnings; 29 // override pure virtual function earnings in Employee 30 double SalariedEmployee::earnings() const 31 { 32 return getWeeklySalary(); 33 } // end function earnings 34 35 // print SalariedEmployee's information 36 void SalariedEmployee::print() const 37 { 38 cout << "salaried employee: "; 39 Employee::print(); // reuse abstract base-class print function 40 cout << "\nweekly salary: " << getWeeklySalary(); 41 } // end function print
1 //CommissionEmployee.h 2 // CommissionEmployee class derived from Employee. 3 #ifndef COMMISSION_H 4 #define COMMISSION_H 5 6 #include "Employee.h" // Employee class definition 7 8 class CommissionEmployee : public Employee 9 { 10 public: 11 CommissionEmployee( const string &, const string &, 12 const string &, double = 0.0, double = 0.0 ); 13 14 void setCommissionRate( double ); // set commission rate 15 double getCommissionRate() const; // return commission rate 16 17 void setGrossSales( double ); // set gross sales amount 18 double getGrossSales() const; // return gross sales amount 19 20 // keyword virtual signals intent to override 21 virtual double earnings() const; // calculate earnings 22 virtual void print() const; // print CommissionEmployee object 23 private: 24 double grossSales; // gross weekly sales 25 double commissionRate; // commission percentage 26 }; // end class CommissionEmployee 27 28 #endif // COMMISSION_H
1 // CommissionEmployee.cpp 2 // CommissionEmployee class member-function definitions. 3 #include <iostream> 4 using std::cout; 5 6 #include "CommissionEmployee.h" // CommissionEmployee class definition 7 8 // constructor 9 CommissionEmployee::CommissionEmployee( const string &first, 10 const string &last, const string &ssn, double sales, double rate ) 11 : Employee( first, last, ssn ) 12 { 13 setGrossSales( sales ); 14 setCommissionRate( rate ); 15 } // end CommissionEmployee constructor 16 17 // set commission rate 18 void CommissionEmployee::setCommissionRate( double rate ) 19 { 20 commissionRate = ( ( rate > 0.0 && rate < 1.0 ) ? rate : 0.0 ); 21 } // end function setCommissionRate 22 23 // return commission rate 24 double CommissionEmployee::getCommissionRate() const 25 { 26 return commissionRate; 27 } // end function getCommissionRate 28 29 // set gross sales amount 30 void CommissionEmployee::setGrossSales( double sales ) 31 { 32 grossSales = ( ( sales < 0.0 ) ? 0.0 : sales ); 33 } // end function setGrossSales 34 35 // return gross sales amount 36 double CommissionEmployee::getGrossSales() const 37 { 38 return grossSales; 39 } // end function getGrossSales 40 41 // calculate earnings; 42 // override pure virtual function earnings in Employee 43 double CommissionEmployee::earnings() const 44 { 45 return getCommissionRate() * getGrossSales(); 46 } // end function earnings 47 48 // print CommissionEmployee's information 49 void CommissionEmployee::print() const 50 { 51 cout << "commission employee: "; 52 Employee::print(); // code reuse 53 cout << "\ngross sales: " << getGrossSales() 54 << "; commission rate: " << getCommissionRate(); 55 } // end function print
1 // BasePlusCommissionEmployee.h 2 // BasePlusCommissionEmployee class derived from Employee. 3 #ifndef BASEPLUS_H 4 #define BASEPLUS_H 5 6 #include "CommissionEmployee.h" // CommissionEmployee class definition 7 8 class BasePlusCommissionEmployee : public CommissionEmployee 9 { 10 public: 11 BasePlusCommissionEmployee( const string &, const string &, 12 const string &, double = 0.0, double = 0.0, double = 0.0 ); 13 14 void setBaseSalary( double ); // set base salary 15 double getBaseSalary() const; // return base salary 16 17 // keyword virtual signals intent to override 18 virtual double earnings() const; // calculate earnings 19 virtual void print() const; // print BasePlusCommissionEmployee object 20 private: 21 double baseSalary; // base salary per week 22 }; // end class BasePlusCommissionEmployee 23 24 #endif // BASEPLUS_H
1 // BasePlusCommissionEmployee.cpp 2 // BasePlusCommissionEmployee member-function definitions. 3 #include <iostream> 4 using std::cout; 5 6 // BasePlusCommissionEmployee class definition 7 #include "BasePlusCommissionEmployee.h" 8 9 // constructor 10 BasePlusCommissionEmployee::BasePlusCommissionEmployee( 11 const string &first, const string &last, const string &ssn, 12 double sales, double rate, double salary ) 13 : CommissionEmployee( first, last, ssn, sales, rate ) 14 { 15 setBaseSalary( salary ); // validate and store base salary 16 } // end BasePlusCommissionEmployee constructor 17 18 // set base salary 19 void BasePlusCommissionEmployee::setBaseSalary( double salary ) 20 { 21 baseSalary = ( ( salary < 0.0 ) ? 0.0 : salary ); 22 } // end function setBaseSalary 23 24 // return base salary 25 double BasePlusCommissionEmployee::getBaseSalary() const 26 { 27 return baseSalary; 28 } // end function getBaseSalary 29 30 // calculate earnings; 31 // override pure virtual function earnings in Employee 32 double BasePlusCommissionEmployee::earnings() const 33 { 34 return getBaseSalary() + CommissionEmployee::earnings(); 35 } // end function earnings 36 37 // print BasePlusCommissionEmployee's information 38 void BasePlusCommissionEmployee::print() const 39 { 40 cout << "base-salaried "; 41 CommissionEmployee::print(); // code reuse 42 cout << "; base salary: " << getBaseSalary(); 43 } // end function print
1 //main.cpp 2 // Processing Employee derived-class objects individually 3 // and polymorphically using dynamic binding. 4 #include <iostream> 5 using std::cout; 6 using std::endl; 7 using std::fixed; 8 9 #include <iomanip> 10 using std::setprecision; 11 12 #include <vector> 13 using std::vector; 14 15 // include definitions of classes in Employee hierarchy 16 #include "Employee.h" 17 #include "SalariedEmployee.h" 18 #include "CommissionEmployee.h" 19 #include "BasePlusCommissionEmployee.h" 20 21 void virtualViaPointer( const Employee * const ); // prototype 22 void virtualViaReference( const Employee & ); // prototype 23 24 int main() 25 { 26 // set floating-point output formatting 27 cout << fixed << setprecision( 2 ); 28 29 // create derived-class objects 30 SalariedEmployee salariedEmployee( 31 "John", "Smith", "111-11-1111", 800 ); 32 CommissionEmployee commissionEmployee( 33 "Sue", "Jones", "333-33-3333", 10000, .06 ); 34 BasePlusCommissionEmployee basePlusCommissionEmployee( 35 "Bob", "Lewis", "444-44-4444", 5000, .04, 300 ); 36 37 cout << "Employees processed individually using static binding:\n\n"; 38 39 // output each Employee's information and earnings using static binding 40 salariedEmployee.print(); 41 cout << "\nearned $" << salariedEmployee.earnings() << "\n\n"; 42 hourlyEmployee.print(); 43 cout << "\nearned $" << hourlyEmployee.earnings() << "\n\n"; 44 commissionEmployee.print(); 45 cout << "\nearned $" << commissionEmployee.earnings() << "\n\n"; 46 basePlusCommissionEmployee.print(); 47 cout << "\nearned $" << basePlusCommissionEmployee.earnings() 48 << "\n\n"; 49 50 // create vector of four base-class pointers 51 vector < Employee * > employees( 3 ); 52 53 // initialize vector with Employees 54 employees[ 0 ] = &salariedEmployee; 55 employees[ 1 ] = &commissionEmployee; 56 employees[ 2 ] = &basePlusCommissionEmployee; 57 58 cout << "Employees processed polymorphically via dynamic binding:\n\n"; 59 60 // call virtualViaPointer to print each Employee's information 61 // and earnings using dynamic binding 62 cout << "Virtual function calls made off base-class pointers:\n\n"; 63 64 for ( size_t i = 0; i < employees.size(); i++ ) 65 virtualViaPointer( employees[ i ] ); 66 67 // call virtualViaReference to print each Employee's information 68 // and earnings using dynamic binding 69 cout << "Virtual function calls made off base-class references:\n\n"; 70 71 for ( size_t i = 0; i < employees.size(); i++ ) 72 virtualViaReference( *employees[ i ] ); // note dereferencing 73 74 return 0; 75 } // end main 76 77 // call Employee virtual functions print and earnings off a 78 // base-class pointer using dynamic binding 79 void virtualViaPointer( const Employee * const baseClassPtr ) 80 { 81 baseClassPtr->print(); 82 cout << "\nearned $" << baseClassPtr->earnings() << "\n\n"; 83 } // end function virtualViaPointer 84 85 // call Employee virtual functions print and earnings off a 86 // base-class reference using dynamic binding 87 void virtualViaReference( const Employee &baseClassRef ) 88 { 89 baseClassRef.print(); 90 cout << "\nearned $" << baseClassRef.earnings() << "\n\n"; 91 } // end function virtualViaReference
3、Output:
4、Under the Hood
When C++ compiles a class that has one or more virtual functions, it builds a virtual function table (vtable) for that class. An executing program uses the vtable to select the proper function implementation each time a virtual function of that class is called.Whenever an object of a class with one or more virtual functions is instantiated, the compiler attaches to the object a pointer to the vtable for that class.The third level of pointers simply contains the handles to the objects that receive the virtual function calls
When the compiler compiles this statement, it determines that the call is indeed being made via a base-class pointer and that print is a virtual function.The compiler determines that print is the second entry in each of the vtables. To locate this entry, the compiler notes that it will need to skip the first entry. Thus, the compiler compiles an offset or displacement into the table of machine-language object-code pointers to find the code that will execute the virtual function call. The size in bytes of the offset depends on the number of bytes used to represent a pointer on an individual platform.
The compiler generates code that performs the following operations
1). Select the ith entry of employees (in this case, the address of object commissionEmployee), and pass it as an argument to function virtualViaPointer. This sets parameter baseClassPtr to point to commissionEmployee.
2). Dereference that pointer to get to the commissionEmployee object—which, as you recall, begins with a pointer to the CommissionEmployee vtable.
3). Dereference commissionEmployee’s vtable pointer to get to the CommissionEmployee vtable
4). Skip the offset of four bytes to select the print function pointer.
5). Dereference the print function pointer to form the “name” of the actual function to execute, and use the function call operator () to execute the appropriate print function, which in this case prints the employee’s type, name, social security number, gross sales and commission rate.
Fig. 13.18’s data structures may appear to be complex, but this complexity is managed by the compiler and hidden from you, making polymorphic programming straightforward.The pointer dereferencing operations and memory accesses that occur on every virtual function call require some additional execution time. The vtables and the vtable pointers added to the objects require some additional memory.
Operator Overloading
1 #include <stdexcept> 2 #include <iostream> 3 #include <string> 4 5 using namespace std; 6 7 int main() 8 { 9 string str1("better "); 10 string str2("late "); 11 string str3="than "; 12 string str4; 13 string str5; 14 15 //copy constructor 16 //This results in a call to class string's copy constructor. 17 string str6(str5); 18 19 //support logic operators:== != > < >= <= 20 if(str1==str2) 21 { 22 cout<<"str1==str2"<<endl; 23 } 24 else if(str1>str2) 25 { 26 cout<<"str1>str2"<<endl; 27 } 28 else if(str1<str2) 29 { 30 cout<<"str1<str2"<<endl; 31 } 32 //support assignment operator:= 33 str4=str1; 34 //self-assignment. 35 str6=str6; 36 //support concatenate operator:+. 37 str5 +=str1+str2+str3+"never"; 38 //subscript operator:[] 39 //class string's [] operator does not perform any bounds checking. 40 str5[0]='B'; 41 str5[7]='L'; 42 str5[12]='T'; 43 str5[17]='N'; 44 cout<<str5<<endl; 45 46 //function "substr" 47 cout<<str5.substr(0,20)<<endl; 48 cout<<str5.substr(0)<<endl; 49 //function "empty" 50 str5.empty()?cout<<"empty"<<endl:cout<<"not empty"<<endl; 51 //function "at" 52 //function 'at' throws an exception if its argument is an invalid subcript. 53 try 54 { 55 str5.at(40)='A'; 56 } 57 catch(out_of_range &e) 58 { 59 cout<<e.what()<<endl; 60 } 61 62 }
Fundamentals Of Operator Overloadding
Operator overloading is not automatic—you must write operator-overloading functions to perform the desired operations. An operator is overloaded by writing a nonstatic member function definition or non-member function definition.When operators are overloaded as member functions, they must be non-static,because they must be called on an object of that class or operate on that object.To use an operator on an object of a class, the operator must be overloaded for that class—with three exceptions:"=" 、"&" 、"," these three operators can be used with every object without overloadding priviously.
As you prepare to overload operator with your own classes, there are several rules and restrictions you should keep in mind:
• The precedence of an operator cannot be changed by overloading. However, parentheses can be used to force the order of evaluation of overloaded operators in an expression.
• The associativity of an operator cannot be changed by overloading—if an operator normally associates from left to right, then so do all of its overloaded versions.
• You cannot change the “arity” of an operator (that is, the number of operands an operator takes)—overloaded nary perators remain unary operators; overloaded binary operators remain binary operators. Operators &, *, + and - all have both unary and binary versions; these unary and binary versions can be separately overloaded.
• You cannot create new operators; only existing operators can be overloaded.
• The meaning of how an operator works on values of fundamental types cannot be changed by operator overloading. For example, you cannot make the + operator ubtract two ints. Operator overloading works only with objects of user-defined
types or with a mixture of an object of a user-defined type and an object of a fundamental type.
• Related operators, like + and +=, must be overloaded separately.
• When overloading (), [], -> or any of the assignment operators, the operator overloading function must be declared as a class member. For all other overloadable operators, the operator overloading functions can be member functions or
non-member functions.
Overload Binary Operator
1、overloadded as non-static member function
A binary operator can be overloaded as a non-static member function with one parameter or as a non-member function with two parameters . A non-member operator function is often declared as friend of a class for performance reasons.
They’re non-member functions because the object of class PhoneNumber must be the operator’s right operand
1 #include <iostream> 2 #include <iomanip> 3 #include <string> 4 using namespace std; 5 6 class PhoneNumber 7 { 8 //'operator<<' and 'operator>>' are declared friend if they meed to access non-public 9 //class members directly for the performance reasons or because the class does not offer 10 //appropriate get functions. 11 friend ostream & operator<<(ostream & output,const PhoneNumber & phone); 12 friend istream & operator>>(istream & input,PhoneNumber & phone); 13 14 private: 15 string areaCode; 16 string exchange; 17 string line; 18 }; 19 20 //'operator<<' and 'operator>>' are declared as non-member functions because 21 //the object of class PhoneNumber is wanted to be the operator's right operand. 22 ostream & operator<<(ostream & output,const PhoneNumber & phone); 23 istream & operator>>(istream & input,PhoneNumber & phone); 24 25 ostream & operator<<(ostream & output,const PhoneNumber & phone) 26 { 27 output<<"("<<phone.areaCode<<")"<<" "<<phone.exchange<<"-"<<phone.line<<endl; 28 return output; 29 } 30 31 istream & operator>>(istream &input,PhoneNumber & phone) 32 { 33 //parentheses space dash are skipped by input's member function 'ignore()'. 34 input.ignore(); 35 //stream manipulator 'setw()' limits the number of characters read into each stream. 36 input>>setw(3)>>phone.areaCode; 37 input.ignore(2); 38 input>>setw(3)>>phone.exchange; 39 input.ignore(1); 40 input>>setw(4)>>phone.line; 41 42 return input; 43 } 44 45 int main() 46 { 47 cout<<"Please enter a phone number in the form:\'(020) 123-4567\'"<<endl; 48 PhoneNumber oPhone; 49 cin>>oPhone; 50 cout<<"output phone number:"<<oPhone; 51 }
Overloading Unary Operators
A unary operator for a class can be overloaded as a non-static member function with no arguments or as a non-member function with one argument that must be an object (or a reference to an object) of the class. Member functions that implement overloaded operators must be non-static so that they can access the non-static data in each object of the class.
1 #ifndef DATE_H_INCLUDED 2 #define DATE_H_INCLUDED 3 #include <iostream> 4 5 using namespace std; 6 7 class Date 8 { 9 friend ostream& operator<<(ostream & output,const Date & date); 10 public: 11 //Constructors and Destructor 12 Date(int=1,int=1,int=2000); 13 ~Date(); 14 15 //Interface 16 bool setDate(int m,int d,int y); 17 Date& operator++();//prefix increment operator 18 //Note that this function return Date object by value 19 Date operator++(int);//postfix increment operator 20 Date& operator+=(int); 21 22 private: 23 //Utility functions 24 static bool isLeapYear(int y); 25 bool isEndOfMonth(int dd); 26 void increment(); 27 28 //Private data. 29 int _year; 30 int _month; 31 int _day; 32 static const string _year2String[13]; 33 static const int _monthDay[13]; 34 }; 35 36 #endif // DATE_H_INCLUDED 37 //------------------------------------------------------------------------------------------- 38 #include <iostream> 39 #include <string> 40 #include <stdexcept> 41 #include "Date.h" 42 43 using namespace std; 44 45 ostream& operator<<(ostream & output,const Date & date) 46 { 47 cout<<date._year2String[date._month]<<" "<<date._day<<" ,"<<date._year; 48 return output; 49 } 50 51 Date::Date(int m,int d,int y) 52 { 53 setDate(m,d,y); 54 } 55 56 Date::~Date() 57 { 58 59 } 60 61 bool Date::setDate(int m,int d,int y) 62 { 63 if(0<m&&13>m) 64 _month=m; 65 else 66 { 67 throw invalid_argument("month out of range."); 68 } 69 70 if( (d>0&&d<=_monthDay[m]) || (isLeapYear(y)&&m==2&&d>0&&d<=29) ) 71 { 72 _day=d; 73 } 74 else 75 { 76 throw invalid_argument("Day out of range."); 77 } 78 79 if(y>0) 80 { 81 _year=y; 82 } 83 else 84 { 85 throw invalid_argument("invalid year."); 86 } 87 88 return true; 89 } 90 91 Date& Date::operator++() 92 { 93 increment(); 94 return *this; 95 } 96 97 //Note that the dummy integer parameter does not has a parameter name. 98 Date Date::operator++(int) 99 { 100 //must occure before increment() called. 101 //hold current state of object. 102 Date temp=*this; 103 increment(); 104 //return a temporary object the contains the original value of the object 105 //before the increment occured. 106 return temp; 107 } 108 109 Date& Date::operator+=(int nDays) 110 { 111 for(int i=0;i<nDays;++i) 112 { 113 increment(); 114 } 115 116 return *this; 117 } 118 119 bool Date::isLeapYear(int y) 120 { 121 if((y%100!=0)&&(y%4==0) || (y%400==0) ) 122 return true; 123 else 124 return false; 125 } 126 127 bool Date::isEndOfMonth(int dd) 128 { 129 if(_month==2&&isLeapYear(_year)) 130 return dd==29; 131 else 132 return dd==_monthDay[_month]; 133 } 134 135 void Date::increment() 136 { 137 if(!isEndOfMonth(_day)) 138 { 139 ++_day; 140 } 141 else 142 { 143 if(_month<12) 144 { 145 ++_month; 146 _day=1; 147 } 148 else 149 { 150 ++_year; 151 _month=1; 152 _day=1; 153 } 154 } 155 } 156 157 const int Date::_monthDay[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; 158 const string Date::_year2String[13]={"","January","February","March","April","May","June", 159 "July","August","September","October","November","December"}; 160 161 //------------------------------------------------------------------------------------------------------ 162 #include <iostream> 163 #include "Date.h" 164 165 using namespace std; 166 167 int main() 168 { 169 Date d1(1,1,1999); 170 Date d2(10,31,2013); 171 172 d1.setDate(1,1,2000); 173 cout<<" d1:"<<d1<<endl; 174 cout<<"++d1:"<<++d1<<endl; 175 cout<<" d1:"<<d1<<endl; 176 177 cout<<"\n\nd1 :"<<d1<<endl; 178 //When the complier sees the postincrementing expression d1++,it generates the 179 //member-function call d1.operator++(0); 180 cout<<"d1++:"<<d1++<<endl; 181 cout<<"d1 :"<<d1<<endl; 182 183 d1++.setDate(1,1,1990); 184 cout<<d1<<endl; 185 186 Date * poDate=new Date; 187 cout<<"\n\n"<<*poDate; 188 Date * poDate_2=new Date(); 189 cout<<"\n\n"<<*poDate_2; 190 191 192 }
Array Class
In this example, we create a powerful Array class that performs range checking to ensure that subscripts remain within the bounds of the Array. The class allows one array object to be assigned to another with the assignment operator. Array objects know their size, so the size does not need to be passed separately to functions that receive Array parameters. Entire Arrays can be input or output with the stream extraction and stream insertion operators, respectively. You can compare Arrays with the equality operators == and !=.
//main.cpp //-------------------------------------------------------------------------------- #include <iostream> #include "Array.h" using namespace std; void outputArray(const Array &); int main() { Array arrayOne(12); //test constructor with default argument. Array arrayTwo; //test overloadded operator << cout<<arrayOne<<endl; cout<<arrayTwo<<endl; //test getSize() function cout<<"arrayTwo's size is:"<<arrayTwo.getSize()<<endl; //test >> Array arrayThird(5); Array arrayFourth(11); cout<<"Enter 16 integers:"<<endl; cin>>arrayThird>>arrayFourth; cout<<arrayThird<<arrayFourth<<endl; //test copy constructor Array arrayFifth(arrayOne); cout<<arrayFifth<<endl; //test overloadded operator [] Array array(6); for(int i=0;i<array.getSize();++i) { array[i]=i; } cout<<array<<endl; //test overloadded operator = Array array_1(8); array_1=array; array=array; cout<<array_1<<endl; //test overloadded operator == != Array array_2(7); array_2==array?cout<<"array_2==array"<<endl:cout<<"array_2!=array"<<endl; array_2!=array?cout<<"array_2!=array"<<endl:cout<<"array_2==array"<<endl; array_1==array?cout<<"array_1==array"<<endl:cout<<"array_1!=array"<<endl; const Array array_const(6); for(int i=0;i<array_const.getSize();++i) { cout<<"array_const["<<i<<"]="<<array_const[i]<<endl; } Array myArray(7); outputArray(myArray); outputArray(3); } //Array.h //-------------------------------------------------------------------------------- #ifndef __ARRAY_H__ #define __ARRAY_H__ #include <iostream> using namespace std; class Array { friend ostream& operator<<(ostream&,const Array&); friend istream& operator>>(istream&,Array&); public: Array(int=10); Array(const Array&);//copy constructor. ~Array(); int getSize() const; //overload operators const Array& operator=(const Array&); bool operator==(const Array&) const; bool operator!=(const Array&) const; //subscript for non-const objcet returns lvalue. int& operator[](int); //subscript for const objcet returns rvalue. int operator[](int) const; private: int _size; int * _pInt; }; #endif // __ARRAY_H__ //Array.cpp //-------------------------------------------------------------------------------- #include <iostream> #include <iomanip> #include <stdexcept> #include <string.h> #include "Array.h" using namespace std; ostream& operator<<(ostream& output,const Array& array) { output<<"Array Int["<<array.getSize()<<"]:\n"; for(int i=0;i<array.getSize();++i) { output<<array._pInt[i]<<"\t"; if((i+1)%8==0 ) { output<<endl; } } //output two array continously without this statment will get and runtime error??? output<<endl; return output; } istream& operator>>(istream& input,Array& array) { for(int i=0;i<array.getSize();++i) { input>>array._pInt[i]; } return input; } Array::Array(int arraySize) { //parameter validation must needded. if(arraySize<0) { throw invalid_argument("array size must be positive.\n"); } else { _size=arraySize; _pInt=new int[_size]; //need to initialize the new created array. //why this function cannot set every element of the array to 0????? //but the for-loop done. //memset(_pInt,'\0',_size); for(int i=0;i<_size;++i) { _pInt[i]=0; } } } //copy constructor. //memberwise assignment is dangerous for classed with pointer members. //so we need to wirte a copy constructor. Array::Array(const Array& array):_size(array._size) { _pInt=new int[_size]; for(int i=0;i<_size;++i) { _pInt[i]=array._pInt[i]; } } Array::~Array() { //release pointer-base array space delete [] _pInt; } int Array::getSize() const { return _size; } //const return avoids (a1=a2)=a3 //avoids self-assigment,note that the size of the array may be different. const Array& Array::operator=(const Array& array) { if(&array!=this) { if(_size!=array._size) { delete [] _pInt; _size=array._size; } _pInt=new int[_size]; for(int i=0;i<_size;++i) { _pInt[i]=array._pInt[i]; } } return *this;//enables x=y=z for example. } //return true if all the private data of class array is the same. bool Array::operator==(const Array& array) const { if(_size!=array._size) return false; for(int i=0;i<_size;++i) { if(_pInt[i]!=array._pInt[i]) return false; } return true; }//end of function operator==() bool Array::operator!=(const Array& array) const { return !(*this==array);//invoke Array::operator== } //subscript for non-const objcet returns lvalue. int& Array::operator[](int n) { if(n<0||n>_size-1) throw invalid_argument("subscript out of range."); return _pInt[n];//reference return; } //subscript for const objcet returns rvalue. int Array::operator[](int n) const { if(n<0||n>=_size) throw invalid_argument("subscript out of range."); return _pInt[n];//copy return; }
Converting between Types
The compiler knows how to perform certain conversions among fundamental types. You can use cast operators to force conversions among fundamental types.But what about user-defined types? The compiler cannot know in advance how to convert among user-defined types, and between user-defined types and fundamental types, so you must specify how to do this. Such conversions can be performed with conversion
constructors—single-argument constructors that turn objects of other types (including fundamental types) into objects of a particular class.A conversion operator (also called a cast operator) can be used to convert an object of one class into an object of another class or into an object of a fundamental type. Such a
conversion operator must be a non-static member function.
1 //declares an overloaded cast operator function for converting an object of user-defined type 2 //A into a temporary char * object. 3 //An overloaded cast operator function does not specify a return type—the return type is the type 4 //to which the object is being converted. 5 // If s is a class object, when the compiler sees the expression static_cast< char * >( s ), the compiler 6 //generates the call s.operator char *() 7 A::operator char *() const; 8 //declare overloaded cast operator functions that can convert an object of user-defined type A 9 //into an object of user-defined type OtherClass. 10 A::operator OtherClass() const;
One of the nice features of cast operators and conversion constructors is that, when necessary, the compiler can call these functions implicitly to create temporary objects. For example, if an object s of a user-defined String class appears in a program at a location where an ordinary char * is expected, such as cout << s;the compiler can call the overloaded cast-operator function operator char * to convert the object into a char * and use the resulting char * in the expression. With this cast operator provided for a String class, the stream insertion operator does not have to be overloaded to output a String using cout.
explicit Constructors
Any single-argument constructor—except a copy constructor—can be used by the compiler to perform an implicit conversion. The constructor’s argument is converted to an object of the class in which theconstructor is defined. The conversion is automatic and you need not use a cast operator. In some situations, implicit conversions are undesirable or errorprone.
1 #include <iostream> 2 #include "Array.h" 3 4 using namespace std; 5 6 void outputArray(const Array &); 7 8 int main() 9 { 10 Array myArray(7); 11 outputArray(myArray); 12 outputArray(3); 13 } 14 15 void outputArray(const Array &array_1) 16 { 17 cout<<array_1<<endl; 18 }
Line 12 calls function outputArray with the int value 3 as an argument. However, this program does not contain a function called outputArray that takes an int argument.So, the compiler determines whether class Array provides a conversion constructor that can convert an int into an Array. Since the Array constructor receives one int argument, the compiler assumes that the constructor is a conversion constructor that can be used to convert the argument 3 into a temporary Array object containing three elements. Then, the
compiler passes the temporary Array object to function outputArray to output the Array’s contents. Thus, even though we do not explicitly provide an outputArray function that receives an int argument, the compiler is able to compile line 12.
C++ provides the keyword explicit to suppress implicit conversions via conversion constructors when such conversions should not be allowed. A constructor that’s declared explicit cannot be used in an implicit conversion.So,what we only have to do is adding a additional keyword explicit into the constructor Array(int=10); defined in in Array.h.
explicit Array( int = 10 ); // default constructor
1 Array myArray(7); 2 outputArray(myArray); 3 //this time ,this statement gets an error. 4 outputArray(3); 5 //demonstrates how the explicit constructor can be used to create a temporary 6 // Array of 3 elements and pass it to function outputArray. 7 outputArray(Array(3));
Templates
Introduction
Function templates and class templates enable you to specify, with a single code segment, an entire range of related (overloaded) functions—called function-template specializations—or an entire range of related classes—called class-template specializations.This technique is called generic programming.Note the distinction between templates and template specializations: Function templates and class templates are like stencils out of which we trace shapes; function-template specializations and class-template specializations are like the separate tracings that all have the same shape, but could, for example, be drawn in different colors.templates are often defined in headers, which are then #included in the appropriate client source-code files. For class templates, this means that the member functions are also defined in the header.
Function Templates
Initially, you write a single function-template definition. Based on the argument types provided explicitly or inferred from calls to this function, the compiler generates separate source-code functions (i.e., function-template specializations) to handle each function call appropriately.
1 // Using template functions. 2 #include <iostream> 3 using std::cout; 4 using std::endl; 5 6 // function template printArray definition 7 template< typename T > 8 void printArray( const T *array, int count ) 9 { 10 for ( int i = 0; i < count; i++ ) 11 cout << array[ i ] << " "; 12 13 cout << endl; 14 } // end function template printArray 15 16 int main() 17 { 18 const int aCount = 5; // size of array a 19 const int bCount = 7; // size of array b 20 const int cCount = 6; // size of array c 21 22 int a[ aCount ] = { 1, 2, 3, 4, 5 }; 23 double b[ bCount ] = { 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7 }; 24 char c[ cCount ] = "HELLO"; // 6th position for null 25 26 cout << "Array a contains:" << endl; 27 28 // call integer function-template specialization 29 printArray( a, aCount ); 30 31 cout << "Array b contains:" << endl; 32 33 // call double function-template specialization 34 printArray( b, bCount ); 35 36 cout << "Array c contains:" << endl; 37 38 // call character function-template specialization 39 printArray( c, cCount ); 40 return 0; 41 } // end main
When the compiler detects a printArray function invocation in the client program (e.g., lines 29 and 34), the compiler uses its overload resolution capabilities to find a definition of function printArray that best matches the function call. In this case, the only printArray function with the appropriate number of parameters is the printArray function template (lines 7–14).Consider the function call at line 29. The compiler compares the type of printArray’s first argument (int * at line 29) to the printArray function template’s first parameter (const T * const at line 8) and deduces that replacing the type parameter T with int would make the argument consistent with the parameter. Then, the compiler substitutes int for T throughout the template definition and compiles a printArray specialization that can display an array of int values.In this example, the template mechanism saves you from having to write two separate overloaded functions with prototypes
void printArray( const int * const, int ); void printArray( const double * const, int ); void printArray( const char * const, int );
Although templates offer software-reusability benefits, remember that multiple functiontemplate specializations and class-template specializations are instantiated in a program (at compile time), despite the fact that the templates are written only once. These copies can consume considerable memory. This is not normally an issue, though, because the code generated by the template is the same size as the code you’d have written to produce the separate overloaded functions.
Function Templates Can Be Overloadded
A function template may be overloaded in several ways. We can provide other function templates that specify the same function name but different function parameters.A function template also can be overloaded by providing nontemplate functions with the same function name but different function arguments.
The compiler performs a matching process to determine what function to call when a function is invoked. First, the compiler tries to find and use a precise match in which the function names and argument types are consistent with those of the function call. If this fails, the compiler determines whether a function template is available that can be used to generate a function-template specialization with a precise match of function name and argument types that are consistent with those of the function call. If such a template is
found, the compiler generates and uses the appropriate function-template specialization. If not, the compiler generates an error message. Also, if there are multiple matches for the function call, the compiler attempts to determine the best match. If there is more than one best match, the call is ambiguous and the compiler generates an error message.
Class Templates
Conventions
1、I have also put underscores before each private element of the class to make it easier to tell what is private, but it is not a requirement of C++. It looks a bit ugly at first, but I find it makes a big difference when you're reading the code! If you follow this convention, just make sure that you do not use a capital letter after the underscore; this prefix may cause conflicts with some compilers. As long as you stick with a lowercase letter after the underscore when declaring private fields or methods, you’ll be fine.
In my own code, I always start with a public section, followed by a private section. This emphasizes that the public section is meant for users of the class (other programmers) because it is the first thing that a user of the class will see.
1 class ChessBoard 2 { 3 public: 4 ChessPiece getPiece (int x, int y); 5 PlayerColor getMove (); 6 void makeMove (int from_x, int from_y, int to_x, int to_y); 7 private: 8 ChessPiece _board[ 8 ][ 8 ]; 9 PlayerColor _whose_move; 10 };
2、After deleting a pointer, it is a good idea to reset it to point to NULL again:
1 delete pInt; 2 pInt =NULL;
3、Typical Class Design
1 Typical Class Design 2 //SysTest.h 3 //----------------------------------------------------------------- 4 #ifndef __SYS_TEST_H__ 5 #define __SYS_TEST_H__ 6 7 #define LOG(msg) std::cout<<__FILE__<<"-"<<__LINE__<<":"<<msg<<std::endl; 8 9 class SysTest 10 { 11 public: 12 SysTest(); 13 ~SysTest(); 14 static SysTest * instance(); 15 bool connectIPC(); 16 private: 17 static SysTest * poInstance; 18 }; 19 20 #endif 21 22 //SysTest.cpp 23 //------------------------------------------------------------------------ 24 #include "SysTest.h" 25 26 SysTest * SysTest::poInstance=0; 27 28 SysTest * SysTest::instance() 29 { 30 if(0==poInstance) 31 { 32 poInstance = new SysTest(); 33 } 34 35 return poInstance; 36 } 37 38 bool SysTest::connectIPC() 39 { 40 41 } 42 43 44 //main.cpp 45 //----------------------------------------------------------------- 46 47 #include "SysTest.h" 48 49 int main() 50 { 51 if(!SysTest::instance()->connectIPC()) 52 { 53 LOG("connect IPC failed"); 54 return -1; 55 } 56 57 }