类的自动转换和强制类型转换
1)如果构造函数中含有类似的拷贝函数:
CStonewt( double lbs )
{
m_nStone = int( lbs ) / Lbs_per_stn;
m_fPds_left = int( lbs ) % Lbs_per_stn + lbs - int( lbs );
m_fPounds = lbs;
}
那么使用
CStonewt myCat;
myCat = 19;
程序将使用构造函数CStonewt( double lbs )来创建一个临时的CStonewt对象,并将19.6作为初始值。随后,采用逐成员赋值方式将该临时对象的内容赋值到myCat中(比如m_nStone = int(
lbs ) / Lbs_per_stn;)。这一过程称为隐式转换,因为他是自动进行的,而不需要显示强制类型转换。如果换成CStonewt( double lbs, int i )有两个参数,因此不能用来转换类型。
#include "stdafx.h" #include "iostream" using namespace std; namespace { class CStonewt { public: CStonewt( double lbs ) { m_nStone = int( lbs ) / Lbs_per_stn; m_fPds_left = int( lbs ) % Lbs_per_stn + lbs - int( lbs ); m_fPounds = lbs; } CStonewt( int stn, double lbs ) { m_nStone = stn; m_fPds_left = lbs; m_fPounds = stn * Lbs_per_stn + lbs; } CStonewt() { m_nStone = m_fPounds = m_fPds_left = 0; } ~CStonewt() { } void show_lbs() const { cout << m_nStone << " stone" << m_fPds_left << " pound\n" << endl;; } void show_stn() const { cout << m_fPounds << " pound\n" << endl; } private: enum { Lbs_per_stn = 14 }; int m_nStone; double m_fPds_left; double m_fPounds; }; } int _tmain(int argc, _TCHAR* argv[]) { CStonewt myCat; myCat = 19; return 0; }
2)将构造函数用作自动类型转换函数似乎是一项不错的特性。不过,当程序员拥有更丰富的C++经验时,将发现这种自动也行并非总是合乎需要的,因为这会导致意外的类型转换。因此,最新
的C++实现新增了一个关键字(explicit),用来关闭这种自动特性。也就是说,可以这样声明构造函数:
explicit CStonewt( double lbs ) { m_nStone = int( lbs ) / Lbs_per_stn; m_fPds_left = int( lbs ) % Lbs_per_stn + lbs - int( lbs ); m_fPounds = lbs; }
但此时仍然可以进行myCat = (CStonewt)19.5;强制类型转换。
3)把CStonewt类对象赋值给int、double变量
要进行这样的操作时,编译器发现右侧是CStonewt类型,而左侧是int、double类型,因此它将查看程序员是否定义了与此匹配的转换函数(如果没有找到这样的定义,编译器将生成错误消息
,指出无法将CStonewt赋给int、double)
如果想要使用这种转换函数,要转换为typeName类型,需要使用这种形式的转换函数:
operator typeName();
注意以下几点:
a、转换函数必须是类方法
b、转换函数不能指定返回类型
c、转换函数不能有参数
#include "stdafx.h" #include "iostream" using namespace std; namespace { class CStonewt { public: explicit CStonewt( double lbs ) { m_nStone = int( lbs ) / Lbs_per_stn; m_fPds_left = int( lbs ) % Lbs_per_stn + lbs - int( lbs ); m_fPounds = lbs; } CStonewt( int stn, double lbs ) { m_nStone = stn; m_fPds_left = lbs; m_fPounds = stn * Lbs_per_stn + lbs; } CStonewt() { m_nStone = m_fPounds = m_fPds_left = 0; } ~CStonewt(){} operator int() const { return int( 100.5 ); } operator double() const { return 999.5 ; } private: enum { Lbs_per_stn = 14 }; int m_nStone; double m_fPds_left; double m_fPounds; }; } int _tmain(int argc, _TCHAR* argv[]) { CStonewt myCat; myCat = (CStonewt)19.5; double fValue = myCat; int iValue = myCat; cout << "iValue is:" << iValue << endl; cout << "fValue is:" << fValue << endl; return 0; }
类和动态内存分配
小插曲 : strlen()返回字符串长度,但不包括末尾的空字符,因此构造函数len + 1,使分配的内存能够存储包含空字符的字符串
1)含有很多隐藏错误的stringBad类
#ifndef _STRINGBAD_H_ #define _STRINGBAD_H_ #include "iostream" #include "string" using namespace std; class StringBad { public: StringBad(); StringBad( const char* str ); ~StringBad(); friend ostream& operator<< ( ostream& os, const StringBad& sb ); private: char* m_str; int m_nLen; public: static int num_strings; }; #endif
StringBad.cpp:
#include "stdafx.h" #include "StringBad.h" int StringBad::num_strings = 0; StringBad::StringBad() { m_nLen = 4; m_str = new char[ 4 ]; num_strings++; strcpy_s( m_str, strlen( "C++" ) + 1,"C++" ); cout << StringBad::num_strings << ": \"" << m_str << "\" object created" << endl; } StringBad::StringBad( const char* str ) { m_nLen = strlen( str ); m_str = new char[ m_nLen + 1 ]; num_strings++; strcpy_s( m_str,m_nLen + 1 , str ); cout << num_strings << ": \"" << m_str << "\" object created" << endl; } StringBad::~StringBad() { if ( m_str ) { delete[] m_str; } num_strings--; cout << "in the StringBad::~StringBad() num_strings is:" << num_strings << endl; } ostream& operator<< ( ostream& os, const StringBad& sb ) { os << "this StringBad str is:" << sb.m_str << endl; os << "this StringBad len is:" << sb.m_nLen << endl; return os; }
testStringBad.cpp:
#include "stdafx.h" #include "iostream" using namespace std; #include "StringBad.h" void callme1( StringBad& rStringBad ) { cout << "in the function callme1" << endl; cout << "in the function callme1" << rStringBad << endl; } void callme2( StringBad stringBad ) { cout << "in the function callme2" << endl; cout << "in the function callme1" << stringBad << endl; } int _tmain(int argc, _TCHAR* argv[]) { StringBad headline1( "Create headline1" ); StringBad headline2( "Create headline2" ); StringBad sport( "Create sport" ); cout << headline1 << endl; cout << headline2 << endl; cout << sport << endl; callme1( headline1 ); cout << headline1 << endl; callme2( headline2 ); cout << headline2 << endl; cout << "Initialize one object to another:" << endl; StringBad sailer = sport; cout << "sailer" << sailer << endl; cout << "Assign one object to anther:" << endl; StringBad knot; knot = headline1; cout << "knot" << knot << endl; cout << "End of main()" << endl; cout << "num_strings is:" << StringBad::num_strings << endl; return 0; }
2)
复制拷贝函数
StringBad& StringBad::operator= ( const StringBad& st) { if( this == &st ) { return *this; } delete[] str; len = st.len; str = new char[ strlen( len + 1 ) ]; str::strcpy( str, st.str ); return *this; }
3)
重写下标运算符
char& operator[] ( int i ) { return m_str[ i ]; } const char& operator[] ( int i ) const { return m_str[ i ]; }
为什么要提供两个版本。原因是m_str是常量,而上述方法无法确保不修改数据。
但在重载时,C++将区分常量和非常量函数的特征标,因此提供了另一个仅供const String对象使用的
operator[]()版本
4)
新的String类
#include "stdafx.h" #include "iostream" #include "string" using namespace std; #define MAXTEMPSIZE 256 class myString { public: myString() { m_nLen = 4; m_str = new char[ m_nLen + 1 ]; strcpy_s( m_str, strlen( "C++" ) + 1, "C++" ); num_string++; cout << "in the myString():" << endl; } myString( char* str ) { m_nLen = strlen( str ); m_str = new char[ m_nLen + 1 ]; strcpy_s( m_str, m_nLen + 1, str ); num_string++; cout << "in the myString( char* str ):" << endl; } myString( const myString& rString ) { m_nLen = strlen( rString.m_str ); m_str = new char[ m_nLen + 1 ]; strcpy_s( m_str, m_nLen + 1, rString.m_str ); num_string++; cout << "in the myString( const myString& rString ):" << endl; } ~myString() { if ( m_str ) { cout << "this m_str is:" << m_str << endl; delete[] m_str; } num_string--; cout << "in the ~myString():" << endl; } static int HowMany() { return num_string; } inline int length() const { return m_nLen; } myString& operator= ( const myString& rString ) { if ( this == &rString ) { return *this; } m_nLen = rString.m_nLen; m_str = new char[ m_nLen + 1 ]; strcpy_s( m_str, m_nLen + 1, rString.m_str ); num_string++; cout << "in the myString& operator= ( const myString& rString ):" << endl; return *this; } myString& operator= ( const char* str ) { m_nLen = strlen( str ); m_str = new char[ m_nLen + 1 ]; strcpy_s( m_str, m_nLen + 1, str ); num_string++; cout << "in the myString& myString& operator= ( const char* str ):" << endl; return *this; } char& operator[] ( int i ) { return m_str[ i ]; } const char& operator[] ( int i ) const { return m_str[ i ]; } friend ostream& operator<< ( ostream& os, const myString& rString ); friend istream& operator>> ( istream& is, myString& rString ); friend bool operator< ( const myString& rLeft, const myString& rRight ); friend bool operator> ( const myString& rLeft, const myString& rRight ); friend bool operator== ( const myString& rLeft, const myString& rRight ); private: int m_nLen; char* m_str; static int num_string; }; int myString::num_string = 0; ostream& operator<< ( ostream& os, const myString& rString ) { os << "this myString m_str is:" << rString.m_str << endl; return os; } istream& operator>> ( istream& is, myString& rString ) { char temp[ MAXTEMPSIZE ]; is.get( temp, MAXTEMPSIZE ); if ( is ) { rString = temp; } while ( is && is.get() != '\n' ) { continue; } return is; } bool operator< ( const myString& rLeft, const myString& rRight ) { return ( strcmp( rLeft.m_str, rRight.m_str ) < 0 ); } bool operator> ( const myString& rLeft, const myString& rRight ) { return ( strcmp( rLeft.m_str, rRight.m_str ) > 0 ); } bool operator== ( const myString& rLeft, const myString& rRight ) { return ( strcmp( rLeft.m_str, rRight.m_str ) == 0 ); } void showString( const myString myStr ) { cout << "in the showString:" << endl; cout << myStr << endl; } void showString2( const myString& myStr ) { cout << "in the showString2:" << endl; cout << myStr << endl; } int _tmain(int argc, _TCHAR* argv[]) { myString headLine1( "create headLine1" ); myString headLine2( "create headLine2" ); cout << headLine1<< endl; cout << headLine2<< endl; myString sport; cout << sport<< endl; myString sport2 = sport; cout << sport2<< endl; showString( headLine1 ); showString( headLine2 ); showString( sport ); showString( sport2 ); showString2( headLine1 ); showString2( headLine2 ); showString2( sport ); showString2( sport2 ); cout << "headLine1 > headLine2:" << ( headLine1 > headLine2 ) << endl; cout << "headLine1 < headLine2:" << ( headLine1 < headLine2 ) << endl; cout << "headLine1 == headLine2:" << ( headLine1 == headLine2 ) << endl; cout << "headLine1 get howmany is:" << headLine1.HowMany() << endl; cout << "headLine2 get length is:" << headLine2.length() << endl; myString headLine3 = "create headLine3"; cout << headLine3<< endl; showString( headLine3 ); showString2( headLine3 ); cout << headLine3<< endl; myString headLine4 = headLine3; cout << headLine4<< endl; showString( headLine4 ); showString2( headLine4 ); cout << headLine4<< endl; cout << "return [] is:" << headLine4[3] << endl; cin >> headLine4[3]; cout << "return [] is:" << headLine4[3] << endl; cout << headLine4<< endl; showString( headLine4 ); showString2( headLine4 ); cout << headLine4<< endl; cout << "========================================" << endl; return 0; }
#include "stdafx.h" #include "iostream" using namespace std; #include "new" #include "string" #define BUF 512 class CJustString { public: CJustString( const string& str = "create CJustString", int iValue = 0 ) { m_strWords = str; m_nNumber = iValue; cout << m_strWords << " constructed" << endl; } ~CJustString() { cout << m_strWords << " destroyed" << endl; } void Show() const { cout << m_strWords << ", " << m_nNumber << endl; } private: string m_strWords; int m_nNumber; }; int _tmain(int argc, _TCHAR* argv[]) { int* buffer = new int[ BUF ]; // char* buffer = new char[ BUF ]; CJustString *pc1, *pc2; pc1 = new ( buffer ) CJustString; pc2 = new CJustString( "Heap1", 20 ); cout << "Memory block address:\n" << "buffer:" << ( void* )buffer << " heap:" << pc2 << endl; cout << "Memory contents:" << endl; cout << pc2 << ":" << endl; pc2->Show(); CJustString *pc3, *pc4; pc3 = new ( buffer ) CJustString( "Bad Idea", 6 ); pc4 = new CJustString( "Heap2", 6 ); cout << "Memory contents:" << endl; cout << pc3 << ":" << endl; pc3->Show(); cout << pc4 << ":" << endl; pc4->Show(); delete pc2; delete pc4; delete[] buffer; cout << "Done" << endl; return 0; }
该程序使用new操作符创建了一个512*4字节的内存换成去,然后使用new操作符在堆中创建两个CJustString对象,并试图使用布局new操作符在内存缓冲区中创建两个CJustString对象。最后
,他使用delete来释放使用new分配的内存。当然这里不需要单独delete pc1,只要delete[] buffer;便可。
7)队列模拟
进一步了解类后,可将这方面的知识用于解决编程问题。Headther银行打算在Food Heap超市开设一个自动柜员机(ATM)。Food Heap超市的管理则担心排队等待使用ATM的人流会干扰超市
而定交通,希望限制排队等待的人数。Headther银行希望对顾客排队等待的时间进行估测。要编写一个程序来模拟这种情况,让超市的管理者可以了解ATM可能造成的影响。
对于这种问题,最自然的方法是使用顾客队列。队列是一种抽象的数据类型(ADT),可以存储有序的项目序列。新项目被添加在队尾,并可以删除队首的项目。队列有点像堆栈,不过堆栈
在同一段进行添加和删除。这使得堆栈是一种后进先出(LIFO)的结构,而队列是先进先出(FIFO)的。从概念上说,队列就好比是收款台或者ATM前面排的队,所以对于上述问题,队列非常合适。因此,工程的任务之一是定义一个Queue类。
队列的项目是顾客。Headther银行的代表介绍:通常,三分之一的顾客只需要一分钟便可获得服务器,三分之一的顾客需要两分钟,另外三分之一的顾客需要三分钟。另外,顾客到达的
时间是随机的,但每个小时使用自动柜员机的顾客数量相当稳定。工程的另外两项任务是:设计一个表示顾客的类:编写一个程序来模拟顾客和队列之间的交互。
队列类:
1.队列存储有序的项目序列
2.队列所能容纳的项目数有一定的限制
3.应当能够创建空队列
4.应当能够检查队列是否为空
5.应当能够检查队列是否满的
6.应当能够在队尾添加项目
7.应当能够在队首删除项目
8.应当能够确定队列中的项目数
设计类时,需要开发公有接口和私有实现
Customer类:
通常ATM客户有很多属性,例如姓名、账户和账户结余。不过,这里的模拟需要使用的唯一一个属性是客户核实进入队列以及客户交易所需的时间。当模拟生成新客户时,程序将创建一个新的
客户对象,并在其中存储客户的到达时间以及一个随机生成的交易时间。当客户到达队首时,程序将记录此时的时间,并将其与进入队列的时间相减,得到客户的等待时间。
模拟:
1、判断是否来了新的客户。如果来了,并且此时队列未满,则将它添加到队列中,否则拒绝客户入队。
2、如果没有客户在进行交易,则选取队列的第一个客户。确定该客户的已等待时间,并将wait_time
计数器设置为新客户所需的办理时间
3、如果客户正在处理中,则将wait_time计数器减1
4、记录各种数据,如获得服务的客户数目、被拒绝的客户数目、排队等候的累积时间以及累积的队列长度等。
当模拟循环结束时,程序将报告各种统计结果
bool newCustomer( double x )
{
return ( rand() * x / RAND_MAX < 1 );
}
其工作原理如下:值RAND_MAX是在cstdlib文件(以前是stdlib.h)中定义的,是rand()函数可能返回的最大值(0是最小值)。假设客户到达的平均间隔时间x为6,则rand()*x / RAND_MAX的值将
位于0到6之间。具体地说,平均每个6次,这个值会有一次小于1。不过,这个函数可能会导致客户到达的时间间隔有时为1分钟,有时为20分钟。这种方法虽然很笨拙,但可使实际情况不同于
有规则地没6分钟来一个客户。如果客户到达的平均时间间隔少于1分钟,则上述方法将无效,但模拟并不是针对这种情况设计的。如果确实需要处理这种情况,最好提高时间分辨率吗,比如
每次循环代表10秒钟。
#include "stdafx.h" #include "iostream" using namespace std; class CCustomer { public: CCustomer() { m_nArrive = 0; m_nProcessTime = 0; } void set( long when ) { m_nProcessTime = rand() % 3 + 1; m_nArrive = when; } inline long when() const { return m_nArrive; } inline long pTime() const { return m_nProcessTime; } private: long m_nArrive; int m_nProcessTime; }; typedef CCustomer Item; class Queue { public: enum { Q_SIZE = 10 }; Queue( int qs ) : m_nQSize( qs ) { front = rear = NULL; m_Items = 0; } ~Queue() { m_Items = 0; Node* temp; while ( NULL != front ) { temp = front; front = front->next; delete temp; } } int queueCount() const { return m_Items; } bool isEmpty() const { return ( 0 == m_Items ); } bool isFull() const { return ( m_Items == m_nQSize ); } bool dequeue( Item& item ) // 出队列 { if ( NULL == front ) { return false; } item = front->item; Node* temp; temp = front; front = front->next; delete temp; m_Items--; if ( 0 == m_Items ) { rear = NULL; } return true; } bool enqueue( const Item& item ) // 入队列 { if ( isFull() ) { return false; } Node* add = new Node; if ( NULL == add ) { return false; } add->item = item; add->next = NULL; if ( NULL == front ) { front = add; } else { rear->next = add; } m_Items++; rear = add; return true; } private: struct Node { Item item; struct Node* next; }; Node* front; Node* rear; int m_Items; const int m_nQSize; Queue( const Queue& rQueue ) : m_nQSize( 0 ) { }; Queue& operator= ( const Queue& rQueue ) { }; }; #include "cstdlib" #include "ctime" const int MIN_PER_HR = 60; bool newCustomer( double x ) { return ( rand() * x / RAND_MAX < 1 ); } int _tmain(int argc, _TCHAR* argv[]) { srand( time( 0 ) ); cout << "Case Study Bank of Heather Automatic Teller" << endl; cout << "Enter maxnum size of queue" << endl; int qs; cin >> qs; Queue line( qs ); // 分配一个当前队列 cout << "Enter the number of simulation hours:"; // 模拟时间 int hours; cin >> hours; long cyclelimit = MIN_PER_HR * hours; cout << "Enter the average number of customers per hour:"; // 每小时 double perhour; cin >> perhour; double min_per_cust; min_per_cust = MIN_PER_HR; Item temp; long turnaways = 0; long customers = 0; long served = 0; long sum_line = 0; int wait_time = 0; long line_wait = 0; for ( int cycle = 0; cycle < cyclelimit; cycle++ ) { if ( newCustomer( min_per_cust ) ) { if ( line.isFull() ) { turnaways++; } else { customers++; temp.set( cycle ); line.enqueue( temp ); } } if ( wait_time <= 0 && !line.isEmpty() ) { line.dequeue( temp ); wait_time = temp.pTime(); line_wait += cycle - temp.when(); served++; } if ( wait_time > 0 ) { wait_time--; } sum_line += line.queueCount(); } //report result if ( customers > 0 ) { cout << "customers accepted:" << customers << endl; cout << "customers served:" << served << endl; cout << " turnaways:" << turnaways << endl; cout << "average queue size:"; cout.precision( 2 ); cout.setf( ios_base::fixed, ios_base::floatfield ); cout.setf( ios_base::showpoint ); cout << ( double )sum_line / cyclelimit << endl; cout << " average wait_time:" << ( double )line_wait / served << " minutes" << endl; } else { cout << "no customers!" << endl; } cout << "Done!" << endl; return 0; }
类
继承-----is-a关系
派生类和基类之间的特殊关系是基于C++继承的底层模型的。实际上,C++有3中继承方法:共有继承、保护继承和私有继承。公有继承是最常用的方式,它建立一种is-a关系,即派生类对
象也是一个基类对象,可以对基类对象执行的任何操作,也可以对派生类对象执行。
公有继承不能建立is-like-a关系,也就是说,它不采用明喻。人们通常说律师就像鲨鱼,但律师并不是鲨鱼。例如,鲨鱼可以在水下生活。所以,不应从Shark类派生出Lawyer类。继承
可以在基类的基础是哪个添加属性,但不能删除基类的属性。在一些情况下,可以设计一个包含公有特征的类,然后以is-a或has-a关系,使用这个类来定义相关的类。
公有继承不简历is-implemented-as-a(作为……来实现)关系。例如,可以使用数组来实现堆栈,但从Array类派生出Stack类是不合适额,因为堆栈不是数组。例如,数组所以不是堆栈的
属性。另外,可以以其他方式实现堆栈,如使用链表。正确的方法是:通过让堆栈含一个私有的Array对象成员,来隐藏数组实现。
公有继承不简历uses-a关系。例如,计算机可以使用激光打印机,但是从Computer类派生出Printer类(或反过来)是没有意义的。不过,可以使用友元函数或类来处理Printer对象和
Computer对象之间的通信。
在C++语言中,完全可以使用公有继承来建立has-a、is-implemented-as-a或uses-a关系,不过,这样做通常或导致编程方面的问题。因此,还是坚持使用is-a关系吧。
多态公有继承的一个例子:
#include "stdafx.h" #include "string" #include "iostream" using namespace std; class Brass { public: Brass( const char* s = "Nullbody", long an = -1, double a = 0.0 ) { m_szCustomerName = new char[ strlen( s ) + 1 ]; memcpy( m_szCustomerName, s, strlen( s ) + 1 ); m_szNumber = an; m_fBalance = a; } virtual ~Brass() { if ( m_szCustomerName ) { delete[] m_szCustomerName; m_szCustomerName = NULL; } } virtual void deposit( double amt ) // 存款 { m_fBalance += amt; } virtual void teller( double amt ) // 取款 { if ( m_fBalance - amt >= 0 ) { m_fBalance -= amt; } } virtual void showAccountInfo() const // 显示账号信息 { ios_base::fmtflags initialState = cout.setf( ios_base::fixed, ios_base::floatfield ); cout.setf( ios_base::showpoint ); cout.precision( 2 ); cout << "CustomerName is:" << m_szCustomerName << endl; cout << "Number is:" << m_szNumber << endl; cout << "Balance is:" << m_fBalance << endl; cout.setf( initialState ); } inline double GetBalance() const { return m_fBalance; } private: char* m_szCustomerName; long m_szNumber; double m_fBalance; // 当前结余 }; class BrassPlus : public Brass { public: BrassPlus( const char* s = "Nullbody", long an = -1, double a = 0.0, double dOverdrawlimit = 500, double dOverdraft = 0.1, double dOverdraw = 0 ) : Brass( s, an, a ) { m_fOverdrawlimit = dOverdrawlimit; m_fOverdraft = dOverdraft; m_fOverdraw = dOverdraw; } BrassPlus( const Brass& rBrass, double dOverdrawlimit = 500, double dOverdraft = 0.1, double dOverdraw = 0 ) : Brass( rBrass ) { m_fOverdrawlimit = dOverdrawlimit; m_fOverdraft = dOverdraft; m_fOverdraw = dOverdraw; } virtual void teller( double amt ) // 取款 { ios_base::fmtflags initialState = cout.setf( ios_base::fixed, ios_base::floatfield ); cout.setf( ios_base::showpoint ); cout.precision( 2 ); double bal = GetBalance(); if ( amt < bal ) { Brass::teller( amt ); } else if ( amt < bal + m_fOverdrawlimit - m_fOverdraw ) { double advance = amt - bal; m_fOverdraw += advance * ( 1.0 + m_fOverdraft ); cout << "Bank advance:$" << advance << endl; cout << "Finance charge:$" << advance * m_fOverdraft << endl; deposit( advance ); Brass::teller( amt ); } else { cout << "Credit limit exceeded. Transaction cancelled" << endl; } cout.setf( initialState ); } virtual void showAccountInfo() const // 显示账号信息 { ios_base::fmtflags initialState = cout.setf( ios_base::fixed, ios_base::floatfield ); cout.setf( ios_base::showpoint ); cout.precision( 2 ); Brass::showAccountInfo(); cout << "m_fOverdrawlimit is:" << m_fOverdrawlimit << endl; cout << "m_fOverdraft is:" << m_fOverdraft << endl; cout << "m_fOverdraw is:" << m_fOverdraw << endl; cout.setf( initialState ); } inline int setOverdrawlimit( double dOverdrawlimit ) { m_fOverdrawlimit = dOverdrawlimit; } inline int setOverdraft( int iOverdraft ) { m_fOverdraft = iOverdraft; } inline int setOverdraw() { m_fOverdraw = 0; } private: double m_fOverdrawlimit; // 透支上限 double m_fOverdraft; // 贷款利率 double m_fOverdraw; // 当前的透支金额 }; int _tmain(int argc, _TCHAR* argv[]) { Brass Piggy( "porcelot Pigg", 381299, 4000.00 ); BrassPlus Hoggy( "Horatio Hogg", 382288, 3000.00 ); Piggy.showAccountInfo(); cout << "\n"; Hoggy.showAccountInfo(); cout << "\n"; cout << "Depositing $1000 into the Hogg Account:" << endl; Hoggy.deposit( 1000.00 ); cout << "\n"; cout << "New balance:$" << Hoggy.GetBalance() << endl; cout << "WithDrawing $4200 from the Piggy Account:" << endl; Piggy.teller( 4200.00 ); cout << "Pigg account balance:$" << Piggy.GetBalance() << endl; cout << "\n"; cout << "teller $4200 from the Hoggy Account:" << endl; Hoggy.teller( 4200.00 ); cout << "\n"; Hoggy.showAccountInfo(); return 0; }
访问控制protected
关键字protected与private相似,在类外只能用公有类成员来访问protected部分中的类成员。protected和private之间的区别只有在基类派生的类中才会表现出来。派生类的成员可以直
接访问基类的保护成员,但不能直接访问基类的私有成员。因此对于外部世界来说,保护成员的行为与私有成员相似;但对于派生类来说,保护成员的行为与公有成员相似。
单例设计模式
#include "stdafx.h" #include "iostream" using namespace std; class CTheSingle { public: static CTheSingle* GetTheOnlyInstance() { static CTheSingle objCTheSingle; return &objCTheSingle; } protected: CTheSingle(){} ~CTheSingle(){} private: int m_nNumber; }; int _tmain(int argc, _TCHAR* argv[]) { CTheSingle* CTest = CTheSingle::GetTheOnlyInstance(); return 0; }
GetTheOnlyInstance()方法尽在第一次被调用时,创建CTheSingle类的一个实例。以这种方式构造的静态对象一直有效,直到程序终止,此时这种静态对象将自动释放。要检索指向这个类的
唯一一个实例的指针,只需要调用静态方法GetTheOnlyInstance(),该方法返回单对象的地址。
因为静态变量在函数调用结束后仍保存在内存中,所以以后在调用GetTheOnlyInstance()时,将返回同一个静态对象的地址。
抽象基类(abstract base class,ABC)
有时候,使用is-a规则并不像看上去的那样简单。例如,假设正在开发一个图形程序,该程序会显示圆和椭圆等。圆是椭圆的一个特殊情况----长轴和短轴等长的椭圆。因此,所有的圆
都是椭圆,可以从Ellipse类排成出Circle类。但涉及到细节时,将发现许多问题。
首先考虑Ellipse类包含的内容。数据成员可以报考椭圆中心的坐标、长半轴、短半轴以及方向角(水平坐标轴与长轴之间的角度)。另外,还可以包括一些移动椭圆、返回椭圆面积、旋转
椭圆以及缩放长半轴和短半轴的方法,但是Circle类从Ellipse类派生出来并不合适,因为很多数据成员根本不需要。
一种解决办法,即从Ellipse类和Circle类中抽象出他们的共性,将这些特性放到一个ABC类中。然后从该ABC派生出Circle类和Ellipse类。这样,便可以使用基类指针数组同时管理
Ellipse对象和Circle对象,即可以使用多台方法。在这里,这两个类的共同点是中心坐标、move()、area()。确实,甚至不能在ABC中实现area(),因为他没有包含必要的数据成员。C++通过
使用纯虚函数(pure virtual function)提供为实现的函数。
应用ABC概念
一个应用ABC的范例,因此这里将这一概念用于Brass和BrassPlus账户,首先定义一个名为AcctABC的ABC。这个类包含Brass和BrassPlus类共有的所有方法和数据成员,而哪些在Brass和
BrassPlus类中的行为不同的方法应呗声明为寻函数。至少应有一个虚函数是纯虚函数,这一才能使AcctABC成为抽象类
#include "stdafx.h" #include "string" #include "iostream" using namespace std; class IAcctABC { public: IAcctABC( const char* s = "Nullbody", long an = -1, double a = 0.0 ) { m_szCustomerName = new char[ strlen( s ) + 1 ]; memcpy( m_szCustomerName, s, strlen( s ) + 1 ); m_szNumber = an; m_fBalance = a; } virtual ~IAcctABC() { if ( m_szCustomerName ) { delete[] m_szCustomerName; m_szCustomerName = NULL; } cout << "AAAAAAAAAAAAAAAAAA" << endl; } void deposit( double amt ) // 存款 { m_fBalance += amt; } inline char* GetCustomerName() const { if ( m_szCustomerName ) { return m_szCustomerName; } return ""; } inline long GetNumber() const { return m_szNumber; } inline double GetBalance() const { return m_fBalance; } ios_base::fmtflags SetFormat() const { ios_base::fmtflags initialState = cout.setf( ios_base::fixed, ios_base::floatfield ); cout.setf( ios_base::showpoint ); cout.precision( 2 ); return initialState; } virtual void teller( double amt ) = 0; virtual void showAccountInfo() const = 0; private: char* m_szCustomerName; long m_szNumber; double m_fBalance; // 当前结余 }; void IAcctABC::teller( double amt ) { m_fBalance -= amt; } class Brass : public IAcctABC { public: Brass( const char* s, long an, double a ) : IAcctABC( s, an, a ) { } ~Brass() { cout << "BBBBBBBBBBBBBBBB" << endl; } virtual void teller( double amt ) // 取款 { if ( IAcctABC::GetBalance() - amt >= 0 ) { IAcctABC::teller( amt ); } } virtual void showAccountInfo() const // 显示账号信息 { ios_base::fmtflags initialState = SetFormat(); cout << "CustomerName is:" << IAcctABC::GetCustomerName() << endl; cout << "Number is:" << IAcctABC::GetNumber() << endl; cout << "Balance is:" << IAcctABC::GetBalance() << endl; cout.setf( initialState ); } }; class BrassPlus : public IAcctABC { public: BrassPlus( const char* s = "Nullbody", long an = -1, double a = 0.0, double dOverdrawlimit = 500, double dOverdraft = 0.1, double dOverdraw = 0 ) : IAcctABC( s, an, a ) { m_fOverdrawlimit = dOverdrawlimit; m_fOverdraft = dOverdraft; m_fOverdraw = dOverdraw; } BrassPlus( const IAcctABC& rIAcctABC, double dOverdrawlimit = 500, double dOverdraft = 0.1, double dOverdraw = 0 ) : IAcctABC( rIAcctABC ) { m_fOverdrawlimit = dOverdrawlimit; m_fOverdraft = dOverdraft; m_fOverdraw = dOverdraw; } virtual void teller( double amt ) // 取款 { ios_base::fmtflags initialState = cout.setf( ios_base::fixed, ios_base::floatfield ); cout.setf( ios_base::showpoint ); cout.precision( 2 ); double bal = GetBalance(); if ( amt < bal ) { IAcctABC::teller( amt ); } else if ( amt < bal + m_fOverdrawlimit - m_fOverdraw ) { double advance = amt - bal; m_fOverdraw += advance * ( 1.0 + m_fOverdraft ); cout << "Bank advance:$" << advance << endl; cout << "Finance charge:$" << advance * m_fOverdraft << endl; deposit( advance ); IAcctABC::teller( amt ); } else { cout << "Credit limit exceeded. Transaction cancelled" << endl; } cout.setf( initialState ); } virtual void showAccountInfo() const // 显示账号信息 { ios_base::fmtflags initialState = SetFormat(); cout << "CustomerName is:" << IAcctABC::GetCustomerName() << endl; cout << "Number is:" << IAcctABC::GetNumber() << endl; cout << "Balance is:" << IAcctABC::GetBalance() << endl; cout << "m_fOverdrawlimit is:" << m_fOverdrawlimit << endl; cout << "m_fOverdraft is:" << m_fOverdraft << endl; cout << "m_fOverdraw is:" << m_fOverdraw << endl; cout.setf( initialState ); } inline int setOverdrawlimit( double dOverdrawlimit ) { m_fOverdrawlimit = dOverdrawlimit; } inline int setOverdraft( int iOverdraft ) { m_fOverdraft = iOverdraft; } inline int setOverdraw() { m_fOverdraw = 0; } private: double m_fOverdrawlimit; // 透支上限 double m_fOverdraft; // 贷款利率 double m_fOverdraw; // 当前的透支金额 }; int _tmain(int argc, _TCHAR* argv[]) { Brass Piggy( "porcelot Pigg", 381299, 4000.00 ); BrassPlus Hoggy( "Horatio Hogg", 382288, 3000.00 ); Piggy.showAccountInfo(); cout << "\n"; Hoggy.showAccountInfo(); cout << "\n"; cout << "Depositing $1000 into the Hogg Account:" << endl; Hoggy.deposit( 1000.00 ); cout << "\n"; cout << "New balance:$" << Hoggy.GetBalance() << endl; cout << "WithDrawing $4200 from the Piggy Account:" << endl; Piggy.teller( 4200.00 ); cout << "Pigg account balance:$" << Piggy.GetBalance() << endl; cout << "\n"; cout << "teller $4200 from the Hoggy Account:" << endl; Hoggy.teller( 4200.00 ); cout << "\n"; Hoggy.showAccountInfo(); return 0; }
在设计ABC之前,首先应开发一个模型----指出编程问题所需的类以及它们之间的相互关系。一种学院派思想认为,如果要设计类继承层次,则只能将那些不会被用作基类的类设计为具体的类。这种方法的设计更清晰,复杂程度更低。
实际应用注意事项:使用ABC实施接口规则
可以将ABC看做是一种必须实施的接口。ABC要求具体派生类覆盖其纯虚函数----迫使派生类遵循ABC所设置的接口规则。这种模型在基于组件的编程模式中很常见,在这种情况下,,使用
ABC使得组件设计人员能够制定“接口规定”,这样确保了从ABC派生的所有组件都至少支持ABC制定的功能。
友元函数
由于友元函数并非类成员,因此不能继承。然而,可能希望派生类的友元函数能够使用基类的友元函数。为此,可以通过强制类型转换将派生类引用或指针转换为基类引用或指针,然后
使用转换后的指针或引用来调用基类的友元函数:
ostream& operator << ( ostream& os, const hasDMC& hs ) { os << ( const baseDMA& )hs; os << "Style:" << hs.style << endl; return os; }
当然也可以使用dynamic_case<>来进行强制类型转换
子类中同时需要new的情况:
#include "stdafx.h" #include "string" #include "iostream" using namespace std; class CTest_A { public: CTest_A( const char* Name ) { m_szName = new char[ strlen( Name ) + 1 ]; strcpy( m_szName, Name ); } CTest_A( const CTest_A& rCTest_A ) { m_szName = new char[ strlen( rCTest_A.m_szName ) + 1 ]; strcpy( m_szName, rCTest_A.m_szName ); } /* CTest_A& operator= ( const CTest_A& rCTest_A ) { if( this == &rCTest_A ) { return *this; } delete[] m_szName; m_szName = new char[ strlen( rCTest_A.m_szName ) + 1 ]; strcpy( m_szName, rCTest_A.m_szName ); return *this; }*/ ~CTest_A() { if ( m_szName ) { delete[] m_szName; } } void showName() const { cout << "this Name is:" << m_szName << endl; } private: char* m_szName; }; class CTest_B : public CTest_A { public: CTest_B( const char* Number, const char* Name ) : CTest_A( Name ) { m_szNumber = new char[ strlen( Number ) + 1 ]; strcpy( m_szNumber, Number ); } CTest_B( const CTest_B& rCTest_B ) : CTest_A( rCTest_B ) { m_szNumber = new char[ strlen( rCTest_B.m_szNumber ) + 1 ]; strcpy( m_szNumber, rCTest_B.m_szNumber ); } /* CTest_B& operator= ( const CTest_B& rCTest_B ) { if( this == &rCTest_B ) { return *this; } delete[] m_szNumber; m_szNumber = new char[ strlen( rCTest_B.m_szNumber ) + 1 ]; strcpy( m_szNumber, rCTest_B.m_szNumber ); CTest_A::operator =( rCTest_B ); return *this; }*/ ~CTest_B() { if ( m_szNumber ) { delete[] m_szNumber; } } void showNumber() const { cout << "this Number is:" << m_szNumber << endl; } private: char* m_szNumber; }; int _tmain(int argc, _TCHAR* argv[]) { CTest_B* test = new CTest_B( "386644", "zeng" ); test->showNumber(); test->showName(); cout << "\n"; CTest_B test2( "386677", "zeng2" ); test2.showNumber(); test2.showName(); cout << "\n"; CTest_B test3( test2 ); test3.showNumber(); test3.showName(); cout << "\n"; CTest_B test4 = test3; test4.showNumber(); test4.showName(); cout << "\n"; CTest_B test5( test4 ); test5 = *test; delete test; test5.showNumber(); test5.showName(); cout << "\n"; return 0; }