本文参考《数据结构、算法与应用 C++语言描述 原书第2版》第一章中复习c++的思路
函数与参数
参数传递
值传递Value Parameters
int calculate(int a,int b,int c){return a+b+b*c+4;}
int m=20, x=10, y=15,z;
z = calculate(m,x,y);//这个过程会将把局部变量abc复制构造成mxy,方法结束后再析构
//效率低(比如大型矩阵传值)
引用传递Reference Parameters
int calculate(int &a,int &b,int &c){return a+b+b*c+4;}
int m=20, x=10, y=15,z;
z = calculate(m,x,y);//这个过程会将把abc指向mxy的地址,比较快
//对于int、float这种简单的基本数据类型,其实可以直接使用值传递;而其他数据类型比较复杂,应该用引用传递
常量引用传递Const Reference Parameters
template <class Ta, class Tb , class Tc>
Ta abc(const Ta& a, const Tb& b, const Tc& c)
{return a+b*c;}
//当函数不会修改实际参数值(只读)的时候可以采用常量引用参数
函数返回方式
值返回Return Value
直接复制一份被返回的东西
引用返回Return Reference
T& X(int i, T& a)
{……
Return a;
}
//只返回一个引用,不会把该引用对应的值复制到返回环境中
只返回一个引用,不会把该引用对应的值复制到环境中
常量引用返回Return Const Reference
Const T& X(int i,T& z)
//返回的结果是一个不变化的对象(???没看明明白)
重载函数
int abc(int a, int b, int c)
{return a+b*c;}
//重载abc
float abc(float a, float b, float c)
{return a+b*c;}
异常
抛出异常
int abc(int a, int b, int c)
{
if(a<=0 || b<=0 || c<=0 )
throw “All parameters should be >0” ; //抛出了一个类型为char*的异常
return a+b*c;
}
捕获处理异常
int main(){
try (cout<<abc(2,0,4)<<endl; )
catch (char* e)//当捕捉到char*类型异常时,执行下面的语句:
{
cout<< “The parameters to abc were 2,0,4 ”<<endl;
cout<< “An exception has been thrown”<<endl;
cout<< e <<endl; //这里把该异常直接cout出来了
return 1;
}
return 0;
}
动态存储空间
C/C++定义了4个内存区间:
代码区
全局变量与静态变量区
局部变量区(栈区)
**动态存储区,即堆(heap)区或自由存储区(free store) **
一般定义变量(或对象)时,编译器是知道它的大小的,可以直接分配内存;这种方式称为静态存储分配。
而有些对象只有运行时才知道大小,编译时就无法指定存储空间,这时要采用动态存储分配(此时记得要释放内存)。动态存储分配都在堆区(动态存储区)进行。
操作符new 可用来进行动态存储分配:
int *y ;//定义一个int类型指针
y=new int;//让它 = 一个指向int大小的动态空间的指针
*y = 10; //赋值操作
//也可以采取以下写法
int *y = new int;
*y = 10;
int *y = new int (10);
int *y;
y = new int (10);
cin >> n;
float *x = new float[n];
//如果数组的大小在编译时是未知的,必须进行动态存储分配。
//创建动态的二维数组(看的有点晕了)
template <class T>
bool make2dArray (T** &x, int Rows, int columns)
{
try {
x = new T* [Rows]; //x是一个数组的指针,这个数组里也全是指针
for (int i = 0 ; i < Rows; i++)
x[i] = new T [columns]; //每一个“行”都是一个动态数组
return true;
}
catch (bad_alloc) {return false;}
}
异常处理
float *x;
try {x = new float [n];}
catch (bad_alloc) { //内存不足 bad_alloc
cerr << "Out of Memory" << endl;
exit (1) ;
}
//二维数组,在编译时数组的行数未知:
char (*c)[5];//(这个写法要记好)
try { c = new char [n][5];}
catch (bad_alloc) {
cerr << "Out of Memory" << endl;
exit (1);
}
操作符 delete 释放由操作符new所分配的空间
int *y = new int (10);
Delete y;
float *x=new float[n];
Delete []x;
//释放动态的二维数组
template <class T>
void delete2dArray( T** &x, int Rows)//这里只需要传入行数就行,因为删除某行的时候,不需要知道它的列数
{
for (int i = 0 ; i < Rows ; i++)
delete [] x[i]; //删除每个x[i],即删除每个行
delete [] x; //最后才删除x
x = NULL;
}
自有数据类型
下面以货币类currency为例(有点长,耐心记下来):
//先枚举出来正负号的表达方式
enum signType {plus, minus};
//声明货币类,和其中的几个方法
class currency {
public:
//构造函数
currency(signType theSign = plus, unsigned long theDollars = 0, unsigned int
theCents = 0); //只声明,未实现
//析构函数
~currency() {}
bool setValue(signType, unsigned long, unsigned int);//只声明,未实现
bool setValue(double);//只声明,未实现
//三个getter函数
signType getSign() const {return sign;}
unsigned long getDollars() const {return dollars;}
unsigned int getCents() const {return cents;}
//下面三个是常量函数(不改变调用对象的值)
currency add(const currency&) const;
currency& increment(const currency&);
void output() const;
private:
signType sign; //符号(资金的正负)
unsigned long dollars; //美元的数量
unsigned int cents; //美分的数量
}
//构造函数的实现
currency::currency(signType theSign, unsigned long theDollars,
unsigned int theCents)
{
if (theCents > 99) //美分太多,抛出异常
throw illegalParameterValue("Cents should be < 100" );
sign = theSign;
dollars = theDollars;
cents = theCents;
}
//两个修改货币数setValue函数的实现
bool currency::setValue((signType theSign, unsigned long
theDollars, unsigned int theCents))
{
if (theCents > 99) //美分太多,抛出异常
throw illegalParameterValue("Cents should be < 100" );
sign = theSign;
dollars = theDollars;
cents = theCents;
return true;
}
bool currency::setValue(double theAmount)
{//给调用对象的数据成员赋值
if (theAmount < 0) {sign = minus; theAmount = -theAmount;}
else sign = plus;
dollars = (unsigned long) theAmount; //提取整数部分
cents = (unsigned int) ((theAmount+0.001-dollars)*100); //提取两个小数位
return true;
}
//合计add函数的实现(方法头上有const,是常量函数,不会改变调用者,而是返回一个新的currency对象)
currency currency::add(const currency& x) const
{//把x和 *this相加.
long a1, a2, a3;
currency result;
//把调用对象转换成符号整数
a1 = dollars * 100 + cents;
if (sign = = minus) a1 = -a1;
//把x转换成符号整数
a2 = x.dollars * 100 + x.cents;
if (x.sign = = minus) a2 = -a2 ;
a3 = a1 + a2;
//转换为currency 对象的表达形式
if (a3 < 0) {result.sign = minus; a3 = -a3 ; }
else result.sign = plus;//这里可以用.直接访问currency的private字段,因为add是currency类的函数
result.dollars = a3/100;
result.cents = a3 - result.dollars * 100;
return result;
}
//资金增加increment函数的实现(调用者的值增加了)
currency& currency::increment(const currency& x)
{
*this = add(x);
return *this;
}
//打印资金output函数的实现
void currency::output ( ) const
{//输出调用对象的值
if (sign = = minus) cout << '-';
cout << '$' << dollars << '.';
if (cents < 10) cout << "0";
cout << cents;
}
假设上文的代码都在currency.h中
下面开始使用currency类:
#include <iostream>
#include "currency.h "
using namespace std;
int main ()
{
currency g, h(plus, 3, 50), i, j;
//使用两种形式的setValue来赋值
g.setValue(minus, 2, 25);
i.setValue ( - 6. 45 ) ;
//调用add和output
j = h.add(g);
h.output( );
cout << " + ";
g.output( );
cout << " = ";
j.output( ); cout << endl;
//连续调用两次函数add
j = i.add(g).add(h);
……//省略输出语句
//调用函数increment和add
j = i. increment(g).add(h);
……//省略输出语句
//测试异常
cout<< " Attempting to inatialize with cents =152" << endl;
try {i.setValue(plus, 3, 152); }
catch (illegalParameterValue e)
{
cout<< “Caught thrown exception" << endl;
e.outputMessage();
}
return 0;
}
操作符重载
class currency {
public :
//构造函数
currency(signType theSign = plus, unsigned long theDollars = 0, unsigned int theCents = 0);
currency operator+(const currency& ) const;
currency& operator+=(const currency& x){amount += x.amount; return *this;}
void output(ostream& out) const;
Private:
long amount;
} ;
//重载加号+
currency currency::operator+(const currency& x) const
{//把参数对象x 和调用对象 *this相加.
currency result;
result.amount = amount + x.amount;
return result;
}
void currency::output(ostream& out) const
{//将货币值插入到输出流 .
long theAmount = amount;
if (theAmount < 0) {out << '-' ; theAmount = -theAmount ; }
long dollars = theAmount / 100; // dollars
out << '$' << dollars << '.' ;
int cents = theAmount - dollars * 100; // cents
if (cents < 10) out << "0";
out << cents;
}
//重载 <<
ostream& operator<<(ostream& out, const currency& x){
x.output(out);
return out;
}
//使用重载后的操作符
j = h+g;
cout << h << " + " << g << " = " << j << endl;
j = i+g+h;
cout << i << " + " << g << " + " << h << " = " << j << endl;
j = (i+=g)+h;
友元和保护性类成员
友元函数:
在友元函数内部,可以访问该类对象的私有成员。
友元类:
一个类 A 可以将另一个类 B 声明为自己的友元,类 B 的所有成员函数就都可以访问类 A 对象的私有成员
//把ostream& operator<<描述为currency类的友元
class currency {
friend ostream& operator <<(ostream&, const currency&);
Public:
………
}
//然后重载 <<运算符,就不用再
ostream& operator<<(ostream& out, const currency& x)
{//把货币值插入到输出流
long theAmount = x.amount;
if (theAmount < 0) {out << '-' ; theAmount = -theAmount;}
long dollars = theAmount / 100; //dollars
out << '$' << dollars << '.' ;
int cents = theAmount - dollars * 100; // cents
if (cents < 10) out << "0";
out << cents;
return out;
}
派生类可以访问保护性类成员
//保护性类成员
class currency
{
friend ostream& operator <<(ostream&, const currency&);
Public:
…
Protected:
… //这里就是保护性类成员
Private:
…
}
异常类illegalParameterValue
class illegalParameterValue
{
public:
illegalParameterValue():message(“Illegal Parameter Value“){}//无参构造函数(冒号后面是初始化)
illegalParameterValue(char* theMessage){message= theMessage;} //含参构造函数
void outputmessage() {cout<<message<<endl;}//一个普通的输出函数
private:
string message;
};
递归函数
递归函数:递归函数是一个自己调用自己的函数。
一个正确的递归函数必须包含:
➢一个基本部分(递归的出口)
➢递归调用部分
//用递归实现的阶乘函数
int f(int n)
{
if (n<=1) return 1; //递归出口
else return n*f(n-1);//调用自身
}
递归优点: 递归简洁、易编写、易懂 易证明正确性
递归缺点: 递归效率低 重复计算多
递归函数改为非递归:
改为非递归的目的是提高效率
单向递归可直接用迭代实现非递归
其他情形必须借助栈实现非递归
标准模板库STL
C++标准模板库(STL)是一个容器、适配器、迭代器 、函数对象和算法的集合,使用STL,应用程序的 设计会简单很多。