数据结构 九月 05, 2022

C++复习

文章字数 3k 阅读约需 21 mins 阅读次数 1000000

本文参考《数据结构、算法与应用 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个内存区间:

  1. 代码区

  2. 全局变量与静态变量区

  3. 局部变量区(栈区)

  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,应用程序的 设计会简单很多。


上一篇 :  
下一篇 :  
0%