nullC++与C#*C++与C# 第四章 面向对象的程序设计(续)* 第四章 面向对象的程序设计(续)null*class Person {
public:
……
protected:
char * name;
int age;
};class Student : public Person {
public:
……
private:
char * school;
char * class;
int id;
};class Employee: public Person {
public:
……
private:
char * company;
char * job;
double salary;
};如何定义代培生?
是一名职员
是一名学生4.7 多继承null*多继承:一个派生类有两个或者两个以上的直接基类class EmployStud : public Student, public Employee {
public:
……
private:
bool way;
};逗号 , 隔开的多个基类null*EmployStud liming;wayschool
……name
……company
……name
……多个基类对象,
二义性:
有两个name/age存储空间浪费
不便于维护null*虚拟继承:无论基类在派生层次中出现多少次,只有一个共享的基类子对象被继承。class Student : virtual public Person {
……
};class Employee: public virtual Person {
……
};class EmployStud : public Student, public Employee {
……
};EmployStud liming;virtual可在继承关键字之前或之后null*IBM库的头文件:
string
list
stackMicrosoft库的头文件:
vector
queue
stack
void main( )
{
//使用数据类型list和queue
}//包含IBM库的头文件
//包含Microsoft库的头文件造成stack定义冲突4.8 名字空间null*名字空间定义
:
namespace 空间名 {
//成员定义
……
……
}所有可以出现在全局域中的声明都可以放在用户声明的名字空间中:类定义、变量声明或定义、函数声明或定义。名字空间括起来的区域称为名字空间域,出现在名字空间域中的各种声明或定义只能在该域中直接使用null*cstring.h
namespace MyString {
class CString {
……
};
CString & operator + ( const char *, CString & );
}cstring.cpp
namespace MyString {
CString::CString ( )
{ …… }
……
CString & operator + ( const char *str1, CString & str2 )
{ …… }
}注意:名字空间的定义不一定是连续的,可以放在不同文件中。对外接口实现null*注意:名字空间可以嵌套定义 mylib.h
namespace MyLib {
class CString {
……
};
namespace MathLib {
class Complex {
……
};
……
}
namespace GraphicLib {
class Circle {
……
};
……
}
}null*名字空间的使用(1):
空间名::成员名域操作符#include “mylib.h”
void main ()
{
CString str ( “hello”);
}MyLib::CString str (“hello”);MyLib::GraphicLib::Circle cir;
……null*名字空间的使用(2):
定义名字空间别名#include “mylib.h”
namespace graph = MyLib::GraphicLib;
void main ()
{
}graph::Circle cir;
……null*名字空间的使用(3):
using 空间名::成员名;#include “mylib.h”
using MyLib::GraphicLib::Circle;
using MyLib::CString;
void main ()
{
……
}CString str;
Circle cir;null*名字空间的使用(4):
using 空间名;#include “mylib.h”
using MyLib;
void main ()
{
……
}CString str;
Circle cir;
Complex cp;null*class iStack {
public:
……
bool pop( int &top_value );
bool push( int value );
bool full();
bool empty();
……
};int value;
iStack st;
……
if(!st.push(value))
cout<<“Full”;
……
if(!st.pop(value))
cout<<“Empty”;
……
if(!st.push(value))
……错误处理和正常操作混在一起4.9 异常(Exception)处理null*异常是程序可以检测到的、运行时刻不正常的情况异常处理定义:
定义异常类型
抛出异常
捕获异常
处理异常null*异常类型:用来定义出错信息,可以是用户自定义类型或者内置数据类型。statckError.h
class StackError {
public:
StackError ( int i = 0, int v = 0)
: state (i), value(v) { }
int value ( ) { return value; }
int state ( ) { return state; }
private:
int state; //0:ok, 1:full, 2:empty
int value; //试图将value压入堆栈
}; null*抛出异常:
throw 异常对象;异常类型的一个实例class iStack {
public:
// 不再返回一个值
void pop( int &value );
void push( int value );
……
}#include “stackError.h”
void iStack::push( int value )
{
if ( full() )
{
StackError error (1, value);
throw error;
}
……
}throw StackError(1,value);
执行throw语句后,便抛出一个异常,程序将从抛出异常位置退出null*捕获和处理异常:
try {
//包含throw语句
……
}
catch ( 异常类型1 [对象] ) {
//处理一种异常
……
}
catch ( 异常类型2 [对象] ) {
//处理另一种异常
……
}
throw语句必须在try块中。
如果throw抛出的异常对象类型与catch后括号内的异常类型一致,则程序执行该catch后的子句null*try {
int value;
iStack st;
……
st.push(value);
……
st.pop(value);
……
st.push(value);
……
}
catch ( StackError err ) {
int st = err.state();
if (st == 1)
……
else if ( st == 2 )
……
else
……
}正常操作与错误处理操作的分离null*异常处理步骤:
try块中如果没有异常抛出,则正常执行,并忽略后面的catch子句。
若try块中有异常抛出,则程序从抛出位置退出;若抛出的异常对象类型与try块后的某个catch声明类型匹配,则执行该catch子句处理异常;处理完异常后,程序将从catch子句列表后的语句继续执行。
若找不到匹配项,则该函数带着一个异常退出,继续检查函数调用点是否有try/catch子句。bool buidStack ( )
{
try {
//push操作
……
}
catch (StackError err ){
……
}
return 1;
} return 0;void clearStack ( )
{
//pop操作
……
}void main ()
{
try {
buildStack();
clearStack();
}
catch (StackError err ) {
……
}
}null*重新抛出异常:在异常处理过程中也可能存在单个catch 子句不能完全处理异常的情况,必须由函数调用链中更上级的函数来处理,那么catch子句可以通过重新抛出该异常,把异常传递给函数调用链中更上级的另一个catch子句。
格式:
throw;void buidStack ( )
{
try {
//push操作
……
}
catch (StackError err ){
……
throw;
}
} void main ()
{
try {
buildStack();
……
}
catch (StackError err ) {
……
}
}抛出的仍然是接受到的errnull*捕获所有异常:
catch ( … ) { }char * str = new char[100];
……
……
……
……
……
delete [ ] str;有可能出现各种各样的异常try {
……
……
}
catch ( … ) {
delete [ ] str;
throw;
}null*#include
void main() {
cout << “Hello world!";
char str[20];
cin >> str;
}4.10 输入、输出流null*库中提供了以下类的定义:
istream类 (输入流)
ostream类 (输出流)
iostream类 (派生自istream和ostream,输入/输出流)同时该库定义了如下对象:
cin : istream类对象,代表标准输入;
cout : ostream类对象,代表标准输出;
cerr : ostream类对象,代表标准错误输出。null*输出:
重载的左移操作符 <<输出操作符可以接受任何内置数据类型的实参;
可以连续输出多个数据。能否输出
类对象的信息?class Person {
public:
……
private:
char * name;
int age;
}ostream & operator << ( ostream & out );friend ostream & operator << (ostream &out, Person & one );null*ostream & operator << (ostream &out, Person & one )
{
out << one.name <<“\t” << one.age;
return out;
}Person li, wang, zhang;
……
cout << li << wang << zhang;null*输入:
重载的右移操作符 >>注意:
当右操作数与实际输入数据类型不一致时,读入失败,返回false。比输出更容易出错。
缺省情况下,以空格、换行、制表符作为分隔符,并丢弃掉。int ivalue;
cin >> ivalue; //当输入小数或字符串时便出错if ( cin>>ivalue )
……null*char ch;
while ( cin >> ch )
;a b c
d//忽略空格和换行符
//读入四个字符操作符noskipws 使输入操作符不跳过空白字符:char ch;
cin >> noskipws;
while ( cin >> ch )
;
cin >> skipws;//读入七个字符null*null*null*null*其它的输入/输出操作:
istream成员函数:
get ( )
getline ( )
read ( )
peek ( )
……
ostream成员函数:
put ( )
write ( )null*下列三种类类型提供了文件支持
ifstream 从istream派生,把一个文件绑到程序上,用来输入
ofstream 从ostream派生,把一个文件绑到程序上,用来输出
fstream 从iostream派生,把一个文件绑到程序上用来输入和输出。
为了使用文件流组件,我们必须包含相关的头文件
#include
由于在fstream 头文件中也包含了iostream 头文件,所以我们不需要同时包含这两个文件4.11 文件操作null*为了打开一个仅被用于输出的文件,我们可以定义一个ofstream 类对象,例如
ofstream outfile( "copy.out", ios_base::out );
注意:
输出模式ios_base::out 或附加模式ios_base::app,缺省为输出模式;
ofstream从ostrearn 类派生,所以所有ostream 操作都可以应用到一个ofstream 类对象上。outfile<<“hello, world”< (const MyString & ) const;
MyString& operator += (const MyString & ); //将两个字符串相连
protected:
int size;
char *myStr;
};
null*作业1(4月18日前提交):
定义一个类UpperCase,要求:
该类继承自MyString类(第四次作业定义的,可作修改)
该类实例表示的字符串中不包含小写字符,遇到小写字符时,将自动转变为大写字符,其它字符不变。例如:
UpperCase str(“Hello world!”); // HELLO WORLD!
str += “ No 25”; // HELLO WORLD! NO 25
能够实现父类的所有行为(考虑哪些可以继承,哪些需要重定义);
能够以下述形式输入、输出字符串:
UpperCase str,str1,str2;
cin>>str1; cout<