数据结构chapter01_C++基础

C++基础

1.2 函数与参数

1.2.1 传值参数

程序1-1 计算一个整数表达式
int Abc(int a, int b, int c)
{
    return a+b+b*c+(a+b-c)/(a+b)+4;
}

1.2.2 模板函数

程序1-2 计算一个浮点数表达式
float Abc(float a, float b, float c)
{
return a+b+b*c+(a+b-c)/(a+b)+4;
}

程序1-3 利用模板函数计算一个表达式
template<class T>
T Abc(T a, T b, T c)
{
return a+b+b*c+(a+b-c)/(a+b)+4;
}

1.2.3 引用参数

程序1-4 利用引用参数计算一个表达式
template<class T>
T Abc(T& a, T& b, T& c)
{
return a+b+b*c+(a+b-c)/(a+b)+4;
}

1.2.4 常量引用参数

程序1-5 利用常量引用参数计算一个表达式
template<class T>
T Abc(const T& a, const T& b, const T& c)
{
return a+b+b*c+(a+b-c)/(a+b)+4;
}

程序1-6 程序1 - 5的一个更通用的版本
template<class Ta, class Tb, class Tc >
Ta Abc (const Ta& a, const Tb& b, const Tc& c)
{
return a+b+b*c+(a+b-c)/(a+b)+4;
}

1.2.5 返回值

如果需要返回一个引用,可以为返回类型添加一个前缀 &。如:
T& X(int i, T& z)
定义了一个函数X,它返回一个引用参数Z。可以使用下面的语句返回z
return z;
这种返回形式不会把z 的值复制到返回环境中。

如果在函数名之前添加关键字c o n s t,那么函数将返回一个常量引用,例如:
const T& X (int i, T& z)

1.2.6 递归函数

1.3 动态存储分配

1.3.1 操作符n e w
C + +操作符n e w可用来进行动态存储分配,该操作符返回一个指向所分配空间的指针。例
如,为了给一个整数动态分配存储空间,可以使用下面的语句来说明一个整型指针变量:
int *y ;
当程序需要使用该整数时,可以使用如下语法来为它分配存储空间:
y = new int;
操作符n e w分配了一块能存储一个整数的空间,并将指向该空间的指针返回给 y, y是对整数指
针的引用,而* y则是对整数本身的引用。为了在刚分配的空间中存储一个整数值,比如 1 0,可
以使用如下语法:
*y = 10;
我们可以把上述三步(说明y, 分配存储空间,为*y 赋值)进行适当的合并,如下例所示:
int *y = new int;
*y = 10;

int *y = new int (10);

int *y;

y = new int (10);

1.3.2 一维数组

例如,一个大小为 n 的一维浮点数组可以按如下方式来创建:
float *x = new float [n];

可以使用如下语
法来访问每个数组元素: x[0], x[1], ..., x[n-1]。

1.3.3 异常处理

在执行语句
float *x = new float [n];
时,如果计算机不能分配足够的空间怎么办?

可以采用try - catch 结构来捕获( c a t c h)
new 所引发的异常:
float *x;
try {x = new float [n];}
catch (xalloc) { // 仅当new 失败时才会进入
cerr << "Out of Memory" << endl;
e x i t ( 1 ) ; }

1.3.4 操作符d e l e t e

下面的语句可以释放分配给 * y的
空间以及一维数组x:
delete y;
delete [ ] x;

1.3.5 二维数组

当形式参数是一个二维数组时,必须指定其第二
维的大小。例如, a[ ][10]是一个合法的形式参数,而a[ ][ ] 不是。

如果在编译时至少有一维是未知的,必须在运行时使用操作符 n e w来创建该数组。一个二
维字符型数组,假定在编译时已知其列数为 5,可采用如下语法来分配存储空间:
char (*c)[5];
try { c = new char [n][5];}
catch (xalloc) {//仅当n e w失败时才会进入
cerr << "Out of Memory" << endl;
exit (1);}


造二维数组时,可以把它看成是由若干行组合起来的,每一行都是一个一维数组,可以按照前
面讨论的方式用n e w来创建,指向每一行的指针可以保存在另外一个一维数组之中。图 1 - 1给出
了建立一个3×5数组所需要的结构。
x[0], x[1], x[2]分别指向第0行,第1行和第2行的第一个元素。所以,如果x是一个字符数组,
那么x [ 0 : 2 ]是指向字符的指针,而x本身是一个指向指针的指针。可用如下语法来说明 x :
char **x;

为了创建如图1 - 1所示的存储结构,可以使用程序 1 - 1 2中的代码,该程序创建一个类型为 T
的二维数组,这个数组有 r o w s行和c o l s列。程序首先为指针 x [ 0 ] , . . . , x [ r o w s - 1 ]申请空间,然后为
数组的每一行申请空间。在程序中操作符 n e w被调用了r o w s + 1次。如果n e w的某一次调用引发
了一个异常,程序控制将转移到c a t c h块中,并返回f a l s e。如果没有出现异常,数组将被成功创
建,函数M a k e 2 D A r r a y返回t r u e。对于所创建的数组 x中的元素,可以使用标准的用法来引用,
如x [ i ] [ j ] ,其中0≤i<r o w s , 0≤j<c o l s。


图1-1 一个3×5数组的存储结构
程序1-12 为一个二维数组分配存储空间
template <class T>
bool Make2DArray ( T ** &x, int rows, int cols)
{// 创建一个二维数组
t r y {
/ /创建行指针
x = new T * [rows];
/ /为每一行分配空间
for (int i = 0 ; i < rows; i++)
x[i] = new int [cols];
return true;
}
catch (xalloc) {return false;}

程序1-13 创建一个二维数组但不处理异常
template <class T>
void Make2DArray( T ** &x, int rows, int cols)
{// 创建一个二维数组
// 不捕获异常
/ /创建行指针
x = new T * [rows];
/ /为每一行分配空间
for (int i = 0 ; i<rows; i++)
x[i] = new int [cols];
}


M a k e 2 D A r r a y按程序1 - 1 3定义时,可以使用如下代码来确定存储分配是否成功:
try { Make2DArray (x, r, c);}
catch (xalloc) {cerr<< "Could bot create x" << endl;
e x i t ( 1 ) ; }

程序1-14 释放由M a k e 2 D A r r a y所分配的空间
template <class T>
void Delete2DArray( T ** &x, int rows)
{// 删除二维数组x
/ /释放为每一行所分配的空间
for (int i = 0 ; i < rows ; i++)
delete [ ] x[i];
/ /删除行指针
delete [] x;
x = 0;
}

注意在程序1 - 1 4中
x被置为0,以便阻止用户继续访问已被释放的空间。

1.4 类

1.4.1 类C u r r e n c y

程序1-15 定义C u r r e n c y类
class Currency {
p u b l i c :
// 构造函数
Currency(sign s = plus, unsigned long d = 0, unsigned int c = 0);
// 析构函数
~Currency() {}
bool Set(sign s, unsigned long d, unsigned int c);
bool Set(float a);
sign Sign() const {return sgn;}
unsigned long Dollars() const {return dollars;}
unsigned int Cents() const {return cents;}
Currency Add(const Currency& x) const;
Currency& Increment(const Currency& x);
void Output() const;
p r i v a t e :
sign sgn;
unsigned long dollars;
unsigned int cents;
} ;

可以采用如下两种方式来创建 C u r r e n c y类对象:
Currency f, g (plus, 3,45), h (minus, 10);
Currency *m = new Currency ( plus, 8, 12);

调用类成员函数的语法如下:
g . S e t ( m i n u s , 3 3 , 0 ) ;
h . S e t ( 2 0 . 5 2 ) ;

 

 

程序1-16 Currency类的构造函数
Currency::Currency(sign s, unsigned long d, unsigned int c)
{// 创建一个C u r r e n c y对象
if(c > 99)
{ / /美分数目过多
cerr << "Cents should be < 100" << endl;
e x i t ( 1 ) ; }
sgn = s; dollars = d; cents = c;
}

程序1 - 1 7给出了两个S e t函数的代码。第一个函数首先验证参数的合法性,如果参数合法,
则用它们来设置p r i v a t e成员变量。第二个函数不执行参数合法性验证,它仅使用小数点后面的
头两个数字。形如 d
1.d2d3 的数可能没有一个精确的计算机表示,例如,用计算机所描述的数
5 . 2 9实际上要比真正的5 . 2 9稍微小一点。当用如下语句
cents = (a - dollars) * 100
抽取cents 成员时,这种描述方法可能会带来一个错误,因为 (a - dollars) * 100稍微小于2 9,
当程序把(a - dollars) * 100转换成一个整数时, c e n t s得到的将是2 8而不是2 9。只要

d1.d2d3的计算机表示与实际值相比不少于 0 . 0 0 1或不多于0 . 0 0 9,就可以采用为a 加上0 . 0 0 1来解决我们的问题。例如,如果 5 . 2 9的计算机表示是 5 . 2 8 9 9 9,那么加上 0 . 0 0 1将得到5 . 2 9 0 9 9 ,由此所计算出的
c e n t s就是2 9。
程序1-17 设置p r i v a t e数据成员
bool Currency::Set(sign s, unsigned long d, unsigned int c)
{// 取值
if (c > 99) return false;
sgn = s; dollars = d; cents = c;
return true;
}
bool Currency::Set(float a)
{// 取值
if (a < 0) {sgn = minus; a = -a;}
else sgn = plus;
dollars = a; // 抽取整数部分
// 获取两个小数位
cents = (a + 0.005 - dollars) * 100;
return true;

}

程序1-18 累加两个C u r r e n c y
Currency Currency::Add(const Currency& x) const
{// 把 x累加到 * t h i s .
long a1, a2, a3;
Currency ans;
// 把当前对象转换成带符号的整数
a1 = dollars * 100 + cents;
if (sgn == minus) a1 = -a1;
// 把x转换成带符号的整数
a2 = x.dollars * 100 + x.cents;
if (x.sgn == minus) a2 = -a 2 ;
a3 = a1 + a2;
// 转换成 currency 形式
if (a3 < 0) {ans.sgn = minus; a3 = -a 3 ; }
else ans.sgn = plus;
ans.dollars = a3 / 100;
ans.cents = a3 - ans.dollars * 100;
return ans;
}

程序1-19 Increment与O u t p u t
Currency& Currency::Increment(const Currency& x)
{// 增加量 x .
*this = Add(x);
return *this;
}
void Currency::Output () const
{// 输出currency 的值
if (sgn == minus) cout << '-';
cout << '$' << dollars << '.';
if (cents < 10) cout << "0";
cout << cents;
}

程序1-20 Currency类应用示例
#include <iostream.h>
#include "curr1.h"
void main (void)
{
Currency g, h(plus, 3, 50), i, j;
g.Set(minus, 2, 25);
i . S e t ( - 6 . 4 5 ) ;
j = h.Add(g);
j.Output(); cout << endl;
i . I n c r e m e n t ( h ) ;
i.Output(); cout << endl;
j = i.Add(g).Add(h);
j.Output(); cout << endl;
j = i.Increment(g).Add(h);
j.Output(); cout << endl;
i.Output(); cout << endl;
}

1.4.2 使用不同的描述方法

程序1-21 Currency类的新定义
class Currency {
p u b l i c :
// 构造函数
1 8 第一部分 预 备 知 识
下载
Currency(sign s = plus, unsigned long d = 0, unsigned int c = 0);
// 析构函数
~Currency() {}
bool Set(sign s, unsigned long d, unsigned int c);
bool Set(float a);
sign Sign() const
{if (amount < 0) return minus;
else return plus;}
unsigned long Dollars() const
{if (amount < 0) return (-amount) / 100;
else return amount / 100;}
unsigned int Cents() const
{if (amount < 0)
return -amount - Dollars() * 100;
else return amount - Dollars() * 100;}
Currency Add(const Currency& x) const;
Currency& Increment(const Currency& x)
{amount += x.amount; return *this;}
void Output() const;
p r i v a t e :
long amount;
} ;

Currency::Currency(sign s, unsigned long d, unsigned int c)
{// 创建Currency 对象
if (c > 99)
{// 美分数目过多
cerr << "Cents should be < 100" << endl;
e x i t ( 1 ) ; }
amount = d * 100 + c;
if (s == minus) amount = -amount;
}
bool Currency::Set(sign s, unsigned long d,
unsigned int c)
{// 取值
if (c > 99) return false;
amount = d * 100 + c;
if (s == minus) amount = -amount;
return true;
}
bool Currency::Set(float a)
{// 取值
sign sgn;
if (a < 0) {sgn = minus; a = -a;}
else sgn = plus;
amount = (a + 0.001) * 100;
if (sgn == minus) amount = -amount;
return true;
}

程序1-23 函数A d d和O u t p u t的新代码

Currency Currency::Add(const Currency& x) const

{// 把x 累加至 * t h i s .

Currency y;

y.amount = amount + x.amount;

return y;

}

void Currency::Output() const

{ / /输出currency 的值

long a = amount;

if (a < 0) {cout << '-' ; a = -a;}

long d = a / 100; // 美元

cout << '$' << d << '.';

int c = a-d * 100; // 美分

if (c < 10) cout << "0";

cout << c;

}

1.4.3 操作符重载

程序1-24 使用操作符重载的类定义
class Currency {
p u b l i c :
// 构造函数
Currency(sign s = plus, unsigned long d = 0, unsigned int c = 0);
// 析构函数
~Currency() {}
bool Set(sign s, unsigned long d, unsigned int c);
bool Set(float a);
sign Sign() const
{if (amount < 0) return minus;
else return plus;}
unsigned long Dollars() const
{if (amount < 0) return (-amount) / 100;
else return amount / 100;}
unsigned int Cents() const
{if (amount < 0)
return -amount - Dollars() * 100;
else return amount - Dollars() * 100;}
Currency operator+(const Currency& x) const;
Currency& operator+=(const Currency& x)
{amount += x.amount; return *this;}
void Output(ostream& out) const;
p r i v a t e :
long amount;
} ;

 

程序1-25 +, O u t p u t和< <的代码
Currency Currency::operator+(const Currency& x) const
{// 把 x 累加至* t h i s .
Currency y;
y.amount = amount + x.amount;
return y;
}
void Currency::Output(ostream& out) const
{// 将currency 的值插入到输出流
long a = amount;
if (a < 0) {out << '-' ; a = -a ; }
long d = a / 100; // 美元
out << '$' << d << '.' ;
int c = a - d * 100; // 美分
if (c < 10) out << "0";
out << c;
}
// 重载< <
ostream& operator<<(ostream& out, const Currency& x)
{x.Output(out); return out;}
程序1 - 2 6是程序1 - 2 0的另一个版本,它假定操作符都已经被重载,程序 1 - 2 4和1 - 2 5的代码
位于文件c u r r 3 . h之中。
程序1-26 操作符重载的应用
#include <iostream.h>
#include "curr3.h"
void main(void)
{
Currency g, h(plus, 3, 50), i, j;
g.Set(minus, 2, 25);
i . S e t (-6 . 4 5 ) ;
j = h + g;
cout << j << endl;
i += h;
cout << i << endl;
j = i + g + h;
cout << j << endl;
j = (i+=g) + h;
cout << j << endl;
cout << i << endl;
}

1.4.4 引发异常
程序1-27 异常类B a d I n i t i a l i z e r s
// 初始化失败
class BadInitializers {
p u b l i c :
BadInitializers() {}
} ;

程序1-28 引发异常
Currency::Currency(sign s, unsigned long d, unsigned int c)
{// 创建一个C u r r e n c y对象
if (c > 99) throw BadInitializers();
amount = d * 100 + c;
if (s == minus) amount = -amount;
}
void Currency::Set(sign s, unsigned long d, unsigned int c)
{// 取值
if (c > 99) throw BadInitializers();
amount = d * 100 + c;
if (s == minus) amount = -amount;
}

1.4.5 友元和保护类成员

在C u r r e n c y类例子中(见程序1 - 2 4),定义了一个成员函数O u t p u t以便于对操作符< <的重载。
定义这个函数是必要的,因为如下函数:
ostream& operator <<(ostream& out, const Currency& x)
不能访问p r i v a t e成员a m o u n t。

我们可以把ostream& operator<<描述为C u r r e n c y类的友元,从而
避免定义附加的函数,这样就把 C u r r e n c y所有成员(包括 p r i v a t e成员及p u b l i c成员)的访问权
都授予了该函数。为了产生友元,需要在 C u r r e n c y类描述中引入friend 语句。为一致起见,总
是把friend 语句放在类标题语句中,如:
class Currency {
friend ostream& operator<< (ostream&, const Currency&);
p u b l i c :

程序1-29 重载友元< <
// 重载 < <
ostream& operator<<(ostream& out, const Currency& x)
{// 把currency 的值插入到输出流
long a = x.amount;
if (a < 0) {out << '-' ; a = -a;}
long d = a / 100; // 美元
out << '$' << d << '.' ;
int c = a - d * 100; // 美分
if (c < 10) out << "0";
out << c;
return out;
}

1.4.6 增加#ifndef, #define和# e n d i f语句

文件c u r r 1 . h (或c u r r 3 . h )的全部内容包含了 C u r r e n c y类的描述及实现细节。在文件头,必须
放上如下语句:
#ifndef Currency_
#define Currency_
而在文件尾需要放上语句:
# e n d i f
这些语句确保C u r r e n c y的代码仅被程序包含( i n c l u d e)和编译一次。

1.5 测试与调试

1.5.1 什么是测试

程序1-30 计算并输出一个二次方程的根
template<class T>
void OutputRoots(T a, T b, T c)
{// 计算并输出一个二次方程的根
T d = b*b-4 * a * c ;
if (d > 0) {// 两个实数根
float sqrtd = sqrt(d);
cout << "There are two real roots "
<< (-b+sqrtd)/(2*a) << " and "
<< (-b-sqrtd)/(2*a)
<< endl;}
else if (d == 0)
// 两个根相同
cout << "There is only one distinct root "
<< -b/(2*a)
<< endl;
else // 复数根
cout << "The roots are complex"
<< endl
<< "The real part is "
<< -b/(2*a) << endl
<< "The imaginary part is "
<< sqrt(-d)/(2*a) << endl;
}

1.5.2 设计测试数据

1. 黑盒法

2. 白盒法

posted @ 2016-03-23 16:01  咸_鱼  Views(176)  Comments(0Edit  收藏  举报