Visual C++ 2010 第6章 程序结构(2)

6.1 函数指针

6.1.1 声明函数指针

  • 声明函数指针的通用形式:
    *return_type (pointer_name) (list_of_parameter_types);
    函数指针声明由3部分组成:

    • 指向函数的返回类型
    • 指针名称(前面是指明其指针身份的型号)
    • 指向函数的形参类型
double (*pfun) (char* , int);
//声明1个指针pfun,指向接受2个类型为 char* 和 int 的实参,返回值类型为double的函数
  • 初始化函数指针:
//用函数名初始化函数指针
long sum (long num1,long num2);
long (*pfun) (long,long) = sum; //sum()函数的地址初始化pfun指针
//声明的指针pfun 指向任何接受2个long类型实参,返回值为long类型的函数。

long (*pfun) (long,long);
long product(long,long);
pfun = product ;
#include <iostream>
using std::cout;
using std::endl;

long sum (long a,long b);
long product(long a,long b);

int main()
{
	long (*pdo_it)(long,long);
	pdo_it = product;
	cout << endl
		 << "3*5 = "<< pdo_it(3,5);

	pdo_it = sum;
	cout << endl
		 << "3*(4+5)+6 = "
		 << pdo_it(product(3,pdo_it(4,5)),6);
	cout << endl;

	return 0;
}

long sum (long a,long b)
{
	return a + b;
}

long product(long a,long b)
{
	return a * b;
}

6.1.2 函数指针作为实参

  • 函数可以拥有类型为函数指针的形参,这样的函数可以调用实参指向的函数。
//处理数组的函数:某些情况下,该函数产生数组中所有数的平方和;
//其他情况下,计算所有数的立方和。

#include <iostream>
using std::cout;
using std::endl;

double squared(double);
double cubed(double);
double sumarray(double array[],int len,double(*pfun)(double));

int main(void)
{
	double array[] = {1.3,2.5,3.5,4.5,5.5,6.5,7.5};
	int len(sizeof array/sizeof array[0]);

	cout << endl << "Sum of squares = "<< sumarray(array,len,squared);
	cout << endl << "Sum of cubed = "<< sumarray(array,len,cubed);
	cout << endl;

	return 0;
}

double squared(double x)
{
	return x * x;
}
double cubed(double x)
{
	return x * x * x;
}
double sumarray(double array[],int len,double(*pfun)(double))
{
	double total(0.0);
	for(int i = 0;i < len;i++)
		total += pfun(array[i]);
	return total;
}

6.1.3 函数指针的数组

double sum(double,double);
double product(double,double);
double difference(double,double);

double (*pfun[3])(double,double) = {sum,product,difference};
// 各个数组元素分别初始化为大括号内初始化列表中对应的函数的地址。

//使用该指针数组的第2个元素调用product() 函数
pfun[1] (2.5,3.5);

6.2 初始化函数形参

//如果在调用函数时省略实参,则函数使用默认值执行;
//如果提供实参,则实参将代替默认值
#include <iostream>
using std::cout;
using std::endl;

void showit(const char message[] = "Something is wrong.");
int main()
{
	const char mymess[] = "The end of the world is nigh.";

	showit(); //显示默认值
	showit("Something is terribly wrong!");
	showit();
	showit(mymess);

	cout<<endl;
	return 0;
}
void showit(const char message[])
{
	cout<< endl
		<< message;
	return;
}

在这里插入图片描述

6.3 异常
异常真正适用于可能出现异常且接近致命状态的环境,而不是在正常事件序列中通常预料的将出现的环境。
异常机制使用了3个新的关键字:

  • try:标识可能出现异常的代码块
  • throw:使异常状态产生
  • catch:标识处理异常的代码块
/计算在某台机器上制造一个零件所需的时间(单位:分钟),每小时制造的
//零件数量已经被记录下来,但这台机器经常停机,停机期间不可能制造任
//何零件。
#include <iostream>
using std::cout;
using std::endl;

int main(void)
{
	int counts[]={34,54,0,27,0,10,0};
	int time(60);

	for(int i = 0;i < sizeof counts/sizeof counts[0];i++)
		try //该代码块用来确定异常可能出现的位置
		{
			cout << endl << "Hour" << i+1;
			if(0 == counts[i])
				throw "Zero count - calculation not possible."; 
				//执行throw语句时,控制权立即传递给catch代码块中的第一条语句
				//如果没有抛出异常,则不执行catch代码块
			cout << "minutes per item: "<< static_cast<double>(time)/counts[i];
		}
		catch(const char aMessage[]) 
		{
			cout<< endl << aMessage << endl;
		}

	return 0;
}

在这里插入图片描述

6.3.1 抛出异常

  • 在try代码块中的任何位置都可以抛出异常,throw语句的操作数决定着异常的类型。throw关键字后面的操作数可以是任何表达式,而表达式结果的类型决定着被抛出异常的类型。
  • 可以从try代码块调用的函数中抛出异常:
//上例中做如下修改:
void testThrow(void)
{
	throw "Zero count - calculation not possible."; 
}
// if 语句后调用函数
if(0== counts[i])
testThrow();

6.3.2 捕获异常

  • catch代码块将捕获的异常,取决于catch关键字后面的()中出现的形参说明。
  • 必须为try代码块提供至少一个catch代码块,且catch代码块必须紧跟try代码块。
  • catch代码块将捕获前面紧邻的try 代码块中出现的任何异常,包括从try 代码块内直接或间接调用的函数中抛出的异常。
  • 如果希望使某个catch代码块处理try代码块中抛出的任何异常,则必须将省略号(…)放入包围异常声明的圆括号内:
    catch (…)
    {
    //code to handle any excption
    }
  • 嵌套的try代码块
    可以在try代码块内嵌套try代码块,如果内部try代码块抛出异常,而该代码块后面又没有对应于被抛出的异常类型的catch代码块,则会搜索外部try 代码块的异常处理程序。
#include <iostream>
using std::cin;
using std::cout;
using std::endl;

int main(void)
{
  int height(0);
  const double inchesToMeters(0.0254);
  char ch('y');

  try                                            // Outer try block
  {
    while('y' == ch || 'Y' == ch)
    {
      cout << "Enter a height in inches: ";
      cin >> height;                             // Read the height to be converted

      try                                        // Defines try block in which
      {                                          // exceptions may be thrown
        if(height > 100)
           throw "Height exceeds maximum";       // Exception thrown
        if(height < 9)
           throw height;                         // Exception thrown

         cout << static_cast<double>(height)*inchesToMeters
              << " meters" << endl;
      }
      catch(const char aMessage[])               // start of catch block which
      {                                          // catches exceptions of type
        cout << aMessage << endl;                // const char[]
      }
      cout << "Do you want to continue(y or n)?";
      cin >> ch;
    }
  }
  catch(int badHeight)
  {
    cout << badHeight << " inches is below minimum" << endl;
  }
  return 0;
}

在这里插入图片描述
在这里插入图片描述

6.3.3 MFC中的异常处理

  • 抛出异常的MFC函数通常抛出 类 类型的异常,需要将异常当作指针,而非具体的异常类型进行捕获。如果抛出的异常是CDBException
    类型,则作为catch 代码块形参出现的类型应该是CDBException* 。

6.4 处理内存分配错误

  • 捕获new操作符抛出的异常
    当不能分配内存时,new操作符抛出的异常是bad_alloc类型。bad_alloc 是new标准头文件中定义的类 类型。
#include <new>
#include <iostream>
using std::bad_alloc;
using std::cout;
using std::endl;

int main()
{
	char* pdata(nullptr);
	size_t count(~static_cast<size_t>(0)/2); 
	// 0是int类型,static_cast<size_t>(0) 转换成size_t类型的0
	// ~ 操作符,对所有位取反,得到一个所有位都是1的 size_t值,对应于size_t可表示的最大值。
	// size_t是无符号类型,该值超出了new操作符一次可以分配的最大容量,除2 使其落入允许范围
	// 但此值仍较大,除非计算机有特别大的内存,否则分配请求必将失败
	try
	{
		pdata = new char[count];
		cout<< "Memory allocated."<<endl;
	}
	catch(bad_alloc &ex) // bad_alloc类型的引用 ex
	{
		cout<< "Memory allocation failed."<<endl
			<< "The information from the exception object is: "
			<< ex.what() << endl; 
		// ex调用what()函数,返回一个字符串来描述导致异常的问题
	}
	delete[] pdata;
	return 0;
}

在这里插入图片描述

6.5 函数重载

6.5.1 函数重载的概念

  • 函数重载:允许使用相同的函数名定义多个函数,条件时这些函数的形参列表各不相同。当调用函数时,编译器基于提供的实参列表选择正确的版本。
  • 一组重载函数中各个函数的形参列表必须是唯一的。
#include <iostream>
using std::cout;
using std::endl;

int max(int array[],int len);
long max(long array[],int len);
double max(double array[],int len);

int main(void)
{
	int small[]={1,24,34,22};
	long medium[]={23,245,123,1,234,2345};
	double large[]={23.0,1.4,2.456,345.5,12.0,21.0};

	int lensmall(sizeof small/sizeof small[0]);
	int lenmedium(sizeof medium/sizeof medium[0]);
	int lenlarge(sizeof large/sizeof large[0]);

	cout << endl << max(small,lensmall);
	cout << endl << max(medium,lenmedium);
	cout << endl << max(large,lenlarge);
	//编译器基于实参列表的类型,选择合适的max()函数版本

	cout << endl;
	return 0;
}

int max(int x[],int len)
{
	int maximum(x[0]);
	for(int i = 1;i < len;i++)
		if(maximum < x[i])
			maximum = x[i];
	return maximum;
}

long max(long x[],int len)
{
	long maximum(x[0]);
	for(int i = 1;i < len;i++)
		if(maximum < x[i])
			maximum = x[i];
	return maximum;
}

double max(double x[],int len)
{
	double maximum(x[0]);
	for(int i = 1;i < len;i++)
		if(maximum < x[i])
			maximum = x[i];
	return maximum;
}

在这里插入图片描述

6.5.2 引用类型和重载选择

void f(int& arg);
void f(int&& arg);
//当实参是lvalue时,编译器将始终选择 f(int& )函数;
//当实参是rvalue时,编译器将选择 f(int&& )版本;
int num(5);
f(num); 			// Calls f(int& )
f(2*num);			// Calls f(int&& )
f(25);				// Calls f(int&& )
f(num++);			// Calls f(int&& )
f(++num);			// Calls f(int& )

6.6 函数模板

  • 函数模板:定义用于生成一组函数的方法的代码。
  • 函数模板有一个或多个类型形参,通过为模板的每个形参提供具体的类型实参来生成具体的函数。

使用函数模板

//为max()函数定义一个模板
template<class T> T max(T x[],int len) 
{
	T maximum(x[0]);
	for(int i = 1;i < len; i++)
		if(maximum < x[i])
			maximum = x[i];
	return maximum ;
}
// template 关键字将代码标识为模板定义;
// template后的< > 包围着以 , 分开的、用来创建具体函数实例的类型形参
// T 前面的关键字class表明,T是该模板的类型形参。class是表示类型的通用术语。 可以使用typename 代替 class

#include <iostream>
using std::cout;
using std::endl;

template<typename T> T max(T x[],int len)
{
	T maximum(x[0]);
	for(int i = 1;i < len; i++)
		if(maximum < x[i])
			maximum = x[i];
	return maximum ;
}

int main(void)
{
	int small[]={1,24,34,22};
	long medium[]={23,245,123,1,234,2345};
	double large[]={23.0,1.4,2.456,345.5,12.0,21.0};

	int lensmall(sizeof small/sizeof small[0]);
	int lenmedium(sizeof medium/sizeof medium[0]);
	int lenlarge(sizeof large/sizeof large[0]);

	cout << endl << max(small,lensmall);
	cout << endl << max(medium,lenmedium);
	cout << endl << max(large,lenlarge);
	//编译器使用模板,为每条输出数组中最大值的语句实例化一个max()的新版本

	cout << endl;
	return 0;
}
// 输出与前面的例子相同

6.7 使用decltype 操作符

  • decltype操作符,可以得到一个表达式的类型
template<class T1,class T2>
auto f(T1 v1[ ], T2 v2[ ],size_t count) ->decltype(v1[0]*v2[0]) 
// ->decltype(v1[0]*v2[0]) 确定任何模板实例的返回类型
{
	decltype(v1[0]*v2[0] sum(0);
	for(size_t i = 0; i < count ; i++)
		sum += v1[i]*v2[i];
	return sum;
}
#include <iostream>
#include <typeinfo> //typeid 操作符在此头文件中
using std::cout;
using std::endl;

template<class T1,class T2>
auto product(T1 v1[],T2 v2[],size_t count) ->decltype(v1[0]*v2[0]) 
{
	decltype(v1[0]*v2[0]) sum(0);
	for(size_t i = 0; i < count ; i++)
		sum += v1[i]*v2[i];
	return sum;
}

int main()
{
	double x[] = {100.5,99.5,88.7,77.8};
	short y[] = {3,4,5,6};
	long z[] = {11L,12L,13L,14L};
	size_t n = 4;
	cout << "Result type is " << typeid(product(x,y,n)).name() << endl;
	cout << "Result is "<< product(x,y,n)<< endl;
	cout << "Result type is " << typeid(product(z,y,n)).name() << endl;
	cout << "Result is "<< product(z,y,n)<< endl;

	return 0;
}
// typeid(变量).name() ,获取变量类型名称

在这里插入图片描述

6.8 使用函数的示例

// 计算器程序
#include <iostream>
#include <cstdlib>  //包含exit()函数定义
#include <cctype>   // 包含isdigit()函数定义
using std::cout;
using std::cin;
using std::endl;

void eatspace(char* str); //清空字符串中的空格
double expr(char* str); //计算表达式的值
double term(char* str,int& index);//获取项值
double number(char* str,int& index);//分析数

const int MAX(80);

int main()
{
	char buffer[MAX] = {0};

	cout << endl
		 << "Welcome to your friendly calculator."
		 << endl
		 << "Enter an expression,or an empty line to quit."
		 << endl;

	for(;;)
	{
		cin.getline(buffer,sizeof buffer);
		eatspace(buffer);

		if(!buffer[0])
			return 0;
		cout << "\t= "<< expr(buffer)
			 << endl << endl;
	}

}
//从字符串中删除空格
void eatspace(char* str)
{
	int i(0);
	int j(0);

	while ((*(str+i) = *(str+j++))!= '\0')
		if(*(str+i)!= ' ')
			i++;
	return;
}
//计算表达式的值
double expr(char* str)
{
	double value(0.0);
	int index(0);

	value = term(str,index);

	for(;;)
	{
		switch(*(str + index++))
		{
		case '\0':
			return value;
		case '+':
			value += term(str,index);
			break;
		case '-':
			value -= term(str,index);
			break;
		default:
			cout << endl
			<< "Arrrgh!*#!! There's an error"
			<< endl;
		exit(1); //异常终止
		}	
	}
}
// 获取项值
// str:被分析的字符串;index:字符串中当前位置的索引
// 第2个参数指定为引用,因为希望该函数能够修改主调程序中index变量的值,
// 以使其指向输入字符串中被处理的这一项最后一个字符后面的字符
double term(char* str,int& index)
{
	double value(0.0);

	value = number(str,index);

	while(true)
	{
		if(*(str + index)== '*')
			value *= number(str,++index);
		else if(*(str + index)== '/')
			value /= number(str,++index);
		else
			break;
	}
	return value;
}
// 分析数
double number(char* str,int& index)
{
	double value(0.0);
	if(!isdigit(*(str+index)))
	{
		cout << endl
			<< "Arrrgh!*#!! There's an error"
			<< endl;
		exit(1);
	}
	while(isdigit(*(str + index)))
	{
		value = 10*value + (*(str + index++)-'0'); //数字字符的ASCII值在48~57之间(对应0~9)
													//某个数字的ASCII代码减去'0'的ASCII值,则该数字将被转换为等价的0~9的数字值
	}
	if(*(str + index)!= '.')
		return value;
	
	double factor(1.0);
	while(isdigit(*(str + (++index))))
	{
		factor *= 0.1;
		value = value + (*(str + index)-'0')*factor;
	}
	return value;
}

在这里插入图片描述

扩展:计算带()的表达式

#include <iostream>
#include <cstdlib>  // 包含exit()函数定义
#include <cctype>   // 包含isdigit()函数定义
#include <cstring>  // 包含strcpy_s()函数定义
using std::cout; 
using std::cin;
using std::endl;

void eatspace(char* str); //清空字符串中的空格
double expr(char* str); //计算表达式的值
double term(char* str,int& index);//获取项值
double number(char* str,int& index);//分析数
char* extract(char* str,int& index); // 获取括号内的子字符串

const int MAX(80);

int main()
{
	char buffer[MAX] = {0};

	cout << endl
		 << "Welcome to your friendly calculator."
		 << endl
		 << "Enter an expression,or an empty line to quit."
		 << endl;

	for(;;)
	{
		cin.getline(buffer,sizeof buffer);
		eatspace(buffer);

		if(!buffer[0])
			return 0;
		cout << "\t= "<< expr(buffer)
			 << endl << endl;
	}

}
//从字符串中删除空格
void eatspace(char* str)
{
	int i(0);
	int j(0);

	while ((*(str+i) = *(str+j++))!= '\0')
		if(*(str+i)!= ' ')
			i++;
	return;
}
//计算表达式的值
double expr(char* str)
{
	double value(0.0);
	int index(0);

	value = term(str,index);

	for(;;)
	{
		switch(*(str + index++))
		{
		case '\0':
			return value;
		case '+':
			value += term(str,index);
			break;
		case '-':
			value -= term(str,index);
			break;
		default:
			cout << endl
			<< "Arrrgh!*#!! There's an error"
			<< endl;
		exit(1); //异常终止
		}	
	}
}
// 获取项值
// str:被分析的字符串;index:字符串中当前位置的索引
// 第2个参数指定为引用,因为希望该函数能够修改主调程序中index变量的值,
// 以使其指向输入字符串中被处理的这一项最后一个字符后面的字符
double term(char* str,int& index)
{
	double value(0.0);

	value = number(str,index);

	while(true)
	{
		if(*(str + index)== '*')
			value *= number(str,++index);
		else if(*(str + index)== '/')
			value /= number(str,++index);
		else
			break;
	}
	return value;
}
// 分析数
double number(char* str,int& index)
{
	double value(0.0);
	if(*(str + index)== '(')
	{
		char* psubstr(nullptr);
		psubstr = extract(str,++index); 
		value = expr(psubstr); //递归调用
		delete[]psubstr;
		return value;
	}

	if(!isdigit(*(str+index)))
	{
		cout << endl
			<< "Arrrgh!*#!! There's an error"
			<< endl;
		exit(1);
	}
	while(isdigit(*(str + index)))
	{
		value = 10*value + (*(str + index++)-'0'); //数字字符的ASCII值在48~57之间(对应0~9)
													//某个数字的ASCII代码减去'0'的ASCII值,
													//则该数字将被转换为等价的0~9的数字值
	}
	if(*(str + index)!= '.')
		return value;
	
	double factor(1.0);
	while(isdigit(*(str + (++index))))
	{
		factor *= 0.1;
		value = value + (*(str + index)-'0')*factor;
	}
	return value;
}

//从字符串中提取()之间的子字符串
char* extract(char* str,int& index) 
{
	char buffer[MAX]; //声明数组临时容纳子字符串
	char* pstr(nullptr); 
	int numL(0); //记录字符串中左括号的数量
	int bufindex(index); //使用该变量和递增过的index值来索引数组buffer

	do
	{
		buffer[index - bufindex] = *(str + index);
		switch(buffer[index - bufindex])
		{
		case ')':
			if(0 == numL)
			{
				buffer[index - bufindex] = '\0';
				++index;
				pstr = new char[index - bufindex];
				if(!pstr)
				{
					cout << "Memory allocation failed,"
						 << " program terminated.";
					exit(1);
				}
				strcpy_s(pstr,index - bufindex,buffer); 
				//buffer指定的字符串复制到pstr指定的地址中,第2个参数是目标字符串的长度
				return pstr;
			}
			else
				numL--;
				break;
		case '(':
			numL++;

			break;
		}
	}while(*(str + index++) != '\0');

	cout << "Ran off the end of the expression,must be bad input."
		 << endl;
	exit(1);
}

在这里插入图片描述

6.9 C++/CLI 编程

  • CLR程序中,异常的throw 和 catch 机制:
    在C++/CLI 程序中,必须使用跟踪句柄来抛出异常。因此总是应该抛出异常对象,并尽可能避免抛出字面值,特别是字符串字面值。
try
{
	throw L"Catch me if you can."; //抛出的是 const wchar_t* 类型的异常
}
catch(String^ ex) // catch 代码块不能捕获这里抛出的对象
{
	Console::WriteLine(L"String^: {0}",ex);
}

//正确的代码1:
try
{
	throw L"Catch me if you can."; //抛出的是 const wchar_t* 类型的异常
}
catch(const wchar_t* ex)  //正确的类型
{
	String^ exc = gcnew String(ex);
	Console::WriteLine(L"wchar_t: {0}",exc);
}
//正确的代码2:
try
{
	throw gcnew String(L"Catch me if you can.");//抛出的是引用该字符串的句柄String^
}
catch(String^ ex) 
{
	Console::WriteLine(L"String^: {0}",ex);
}

6.9.1 理解泛型函数

  • 泛型函数,说明本身将被编译,当调用某个与泛型函数说明匹配的函数时,实际类型将在运行时代替类型形参。在编译时没有生成其他代码,也不会发生可能随函数模板出现的代码膨胀问题。
    1.定义泛型函数
//使用类型形参定义泛型函数,当调用函数时实际类型将代替这行形参。
generic<typename T> where T:IComparable
T MaxElement(array<T>^ x)
{
	T max(x[0]);
	for (int i = 1; i < x->Length; i++)
		if(max->CompareTo(x[i])<0)
			max = x[i];
	return max;
}
// 关键字generic将后面跟着的内容指定为泛型函数说明;
// 第一行将函数的类型形参定义成T;
// <>之间的typename 表明后面的T是这个泛型函数中类型形参的名称;
// 使用泛型函数时实际类型将代替这里的类型形参;
// 注意:对于多个类型形参的泛型函数,所有形参名称都要放在<>括号之间,每个名称前面都要有关键字typename,相互之间以逗号分开。
// where 关键字,引入对可能在使用泛型函数时代替T的实际类型的约束;
// 任何将在泛型函数中代替T的类型都必须实现IComparable接口,该约束意味着满足条件的实际类型必须实现CompareTo()函数,以允许该类型的2个队形进行比较;
// 有了这项约束,就可以使用CompareTo()函数来比较max 和某个数组元素。
// 当调用CompareTo函数的对象 (max)< 实参时,该函数返回一个 小于0的整数;
// 如果 max = 实参,则该函数返回0;
// 如果 max > 实参,则该函数返回大于0的整数。
// 第2行给出泛型函数的名称 MaxElement、返回类型T 和 形参列表。

2. 使用泛型函数
调用泛型函数:

  1. 像普通函数一样调用
array<double>^ data = {1.5,3.5,6.7,4.2,2.1};
double maxData = MaxElement(data);
//编译器能够推断出该泛型函数的类型实参时double,并相应的生成调用函数的代码
  1. 编译器无法根据对泛型函数的调用推断出类型实参时,调用时可以在函数名后面的<>内显式的指定类型实参。
double maxData = MaxElement<double> (data);
  • 可以作为类型实参提供给泛型函数的类型限制:
    类型实参不能是本地C++类 类型;
    不能是本地指针或引用;
    不能是值类类型的句柄;
    只能是 int 、double 这样的值类类型,像String^ 这样的跟踪句柄。
// 计算器程序.cpp: 主项目文件。

#include "stdafx.h"
#include <cstdlib> //包含exit()函数

using namespace System;
String^ eatspaces(String^str);
double expr(String^ str);
double term(String^ str,int^ index);
double number(String^ str,int^ index);
String^ extract(String^ str,int^ index);

int main(array<System::String ^> ^args)
{
	String^ buffer;
	Console::WriteLine(L"Welcome to your friendly calcaulator.");
	Console::WriteLine(L"Enter an expression,or an empty line to quit.");

	for(;;)
	{
		buffer = eatspaces(Console::ReadLine());
		if(String::IsNullOrEmpty(buffer)) //IsNullOrEmpty()函数,如果传递的实参字符串为空或长度为0,该函数返回true
		return 0;
		
		Console::WriteLine(L" = {0}\n\n",expr(buffer));
	}
    return 0;
}

// 删除输入字符串中的空格
// 使用System::String类定义的replace()函数将str中的空格全部替换为空字符串
// Replace()函数返回删除空格之后的新字符串
String^ eatspaces(String^str)
{
	return str->Replace(L" ",L"");
}

// 计算算术表达式的值

double expr(String^ str)
{
	//int^ index(0); //声明句柄 ,用*解除引用后可以进行算术运算
				   //这条语句依靠数值0的自动转换来产生该句柄引用的Int32数值类对象
				   // 消除警告 改写为:int^ index(gcnew int(0)); 显式的使用构造函数创建对象,并将其初始化为0
	int^ index(gcnew int(0));
	double value(term(str,index));

	while(*index < str->Length)
	{
		switch(str[*index])
		{
		case '+':
			++(*index);
			value += term(str,index);
			break;
		case '-':
			++(*index);
			value -= term(str,index);
			break;
		default:
			Console::WriteLine(L"Arrrgh!*#!! There's an error.\n");
			exit(1);
		}
	}
	return value;
}

// 获得项值
double term(String^ str,int^ index)
{
	double value(number(str,index));
	while(*index < str->Length)
	{
		if(L'*' == str[*index])
		{
			++(*index);
			value *= number(str,index);
		}
		else if(L'/' == str[*index])
		{
			++(*index);
			value /= number(str,index);
		}
		else 
			break;
	}
	return value;
}

//计算数的值
double number(String^ str,int^ index)
{
	double value(0.0);
	if(L'('== str[*index])
	{
		++(*index);
		String^ substr = extract(str,index);
		return expr(substr);
	}

	if(!Char::IsDigit(str,*index)) // Char类中的IsDigit函数当某个字符是数字时返回true,否则返回false
	{
		Console::WriteLine(L"Arrrgh!*#!! There's an error.\n");
		exit(1);
	}

	while((*index < str->Length)&& Char::IsDigit(str,*index))
	{
		value = 10.0*value + Char::GetNumericValue(str[(*index)]);
		// Char类中的 GetNumericValue()函数返回作为实参传递给它的Unicode数字字符的double值
		++(*index);
	}
	if((*index)== str->Length|| str[*index] != '.')
		return value;
	double factor(1.0);
	++(*index);

	while((*index < str->Length)&& Char::IsDigit(str,*index))
	{
		factor *= 0.1;
		value = value + Char::GetNumericValue(str[*index])*factor;
		++(*index);
	}
	return value;
}

//提取括号内的子字符串
String^ extract(String^ str,int^ index)
{
	int numL(0);
	int bufindex(*index);

	while(*index < str->Length)
	{
		switch(str[*index])
		{
		case ')':
			if (0 == numL)
				return str-> Substring(bufindex,(*index)++ - bufindex); 
				// Substring()提取括号中的子字符串,然后返回此子字符串
			else 
				numL--;
			break;

		case '(':
			numL++;
			break;
		}
		++(*index);
	}
	Console::WriteLine(L"Ran off the end of the expression,must be bad input");
	exit(1);
}

6.12 本章主要内容
在这里插入图片描述

To be continue…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
visualC++2010入门经典源代码 第1 使用visual c++ 2010编程 1.1 .net framework 1 1.2 clr 2 1.3 编写c++应用程序 3 1.4 学习windows编程 4 1.4.1 学习c++ 4 1.4.2 c++标准 5 1.4.3 属性 5 1.4.4 控制台应用程序 5 1.4.5 windows编程概念 6 1.5 集成开发环境简介 7 1.5.1 编辑器 8 1.5.2 编译器 8 1.5.3 链接器 8 1.5.4 库 8 1.6 使用ide 8 1.6.1 工具栏选项 9 1.6.2 可停靠的工具栏 10 1.6.3 文档 11 1.6.4 项目和解决方案 11 1.6.5 设置visual c++ 2010的选项 23 1.6.6 创建和执行windows应用程序 23 1.6.7 创建windows forms应用程序 26 1.7 小结 27 1.8 本主要内容 28 第2 数据、变量和计算 29 2.1 c++程序结构 29 2.1.1 main()函数 36 2.1.2 程序语句 36 2.1.3 空白 38 2.1.4 语句块 38 2.1.5 自动生成的控制台程序 39 2.2 定义变量 40 2.2.1 命名变量 40 2.2.2 声明变量 41 2.2.3 变量的初始值 42 2.3 基本数据类型 42 2.3.1 整型变量 43 2.3.2 字符数据类型 44 2.3.3 整型修饰符 45 2.3.4 布尔类型 46 2.3.5 浮点类型 46 2.3.6 字面值 47 2.3.7 定义数据类型的同义词 48 2.3.8 具有特定值集的变量 49 2.4 基本的输入/输出操作 50 2.4.1 从键盘输入 50 2.4.2 到命令行的输出 50 2.4.3 格式化输出 51 2.4.4 转义序列 52 2.5 c++中的计算 54 2.5.1 赋值语句 54 2.5.2 算术运算 55 2.5.3 计算余数 59 2.5.4 修改变量 60 2.5.5 增量和减量运算符 60 2.5.6 计算的顺序 63 2.6 类型转换和类型强制转换 64 2.6.1 赋值语句中的类型转换 65 2.6.2 显式类型转换 65 2.6.3 老式的类型强制转换 66 2.7 auto关键字 66 2.8 查看类型 67 2.9 按位运算符 67 2.9.1 按位and运算符 68 2.9.2 按位or运算符 69 2.9.3 按位eor运算符 71 2.9.4 按位not运算符 71 2.9.5 移位运算符 71 2.10 lvalue和rvalue 73 2.11 了解存储时间和作用域 74 2.11.1 自动变量 74 2.11.2 决定变量声明的位置 76 2.11.3 全局变量 77 2.11.4 静态变量 80 2.12 名称空间 80 2.12.1 声明名称空间 81 2.12.2 多个名称空间 82 2.13 c++/cli编程 84 2.13.1 c++/cli特有的基本数据类型 84 2.13.2 命令行上的c++/cli输出 87 2.13.3 c++/cli特有的功能—— 格式化输出 88 2.13.4 c++/cli的键盘输入 91 2.13.5 使用safe_cast 92 2.13.6 c++/cli枚举 92 2.14 查看c++/cli类型 96 2.15 小结 97 2.16 练习 97 2.17 本主要内容 98 第3 判断和循环 101 3.1 比较数据值 101 3.1.1 if语句 102 3.1.2 嵌套的if语句 104 3.1.3 嵌套的if-else语句 107 3.1.4 逻辑运算符和表达式 109 3.1.5 条件运算符 112 3.1.6 switch语句 113 3.1.7 无条件转移 116 3.2 重复执行语句块 117 3.2.1 循环的概念 117 3.2.2 for循环的变体 119 3.2.3 while循环 126 3.2.4 do-while循环 128 3.2.5 嵌套的循环 129 3.3 c++/cli编程 132 3.4 小结 137 3.5 练习 138 3.6 本主要内容 138 第4 数组、字符串和指针 139 4.1 处理多个相同类型的数据值 139 4.1.1 数组 140 4.1.2 声明数组 140 4.1.3 初始化数组 143 4.1.4 字符数组和字符串处理 144 4.1.5 多维数组 147 4.2 间接数据访问 150 4.2.1 指针的概念 150 4.2.2 声明指针 150 4.2.3 使用指针 152 4.2.4 初始化指针 152 4.2.5 sizeof操作符 158 4.2.6 常量指针和指向常量的指针 159 4.2.7 指针和数组 161 4.3 动态内存分配 168 4.3.1 堆的别名—— 空闲存储器 168 4.3.2 new和delete操作符 168 4.3.3 为数组动态分配内存 169 4.3.4 多维数组的动态分配 171 4.4 使用引用 172 4.4.1 引用的概念 172 4.4.2 声明并初始化lvalue引用 172 4.4.3 声明并初始化rvalue引用 173 4.5 字符串的本地c++库函数 174 4.5.1 查找以空字符结尾的字符串的长度 174 4.5.2 连接以空字符结尾的字符串 174 4.5.3 复制以空字符结尾的字符串 176 4.5.4 比较以空字符结尾的字符串 177 4.5.5 搜索以空字符结尾的字符串 177 4.6 c++/cli编程 179 4.6.1 跟踪句柄 180 4.6.2 clr数组 181 4.6.3 字符串 195 4.6.4 跟踪引用 203 4.6.5 内部指针 204 4.7 小结 206 4.8 练习 206 4.9 本主要内容 207 第5 程序结构(1) 209 5.1 理解函数 209 5.1.1 需要函数的原因 210 5.1.2 函数的结构 210 5.1.3 使用函数 213 5.2 给函数传递实参 216 5.2.1 按值传递机制 216 5.2.2 给函数传递指针实参 217 5.2.3 给函数传递数组 219 5.2.4 给函数传递引用实参 222 5.2.5 使用const修饰符 224 5.2.6 rvalue引用形参 225 5.2.7 main()函数的实参 227 5.2.8 接受数量不定的函数实参 229 5.3 从函数返回值 231 5.3.1 返回指针 231 5.3.2 返回引用 233 5.3.3 函数中的静态变量 236 5.4 递归函数调用 238 5.5 c++/cli编程 240 5.5.1 接受数量可变实参的函数 241 5.5.2 main( )的实参 242 5.6 小结 243 5.7 练习 243 5.8 本主要内容 244 第6 程序结构(2) 245 6.1 函数指针 245 6.1.1 声明函数指针 246 6.1.2 函数指针作为实参 249 6.1.3 函数指针的数组 250 6.2 初始化函数形参 250 6.3 异常 252 6.3.1 抛出异常 253 6.3.2 捕获异常 254 6.3.3 mfc中的异常处理 255 6.4 处理内存分配错误 256 6.5 函数重载 257 6.5.1 函数重载的概念 258 6.5.2 引用类型和重载选择 260 6.5.3 何时重载函数 260 6.6 函数模板 261 6.7 使用decltype操作符 263 6.8 使用函数的示例 265 6.8.1 实现计算器 265 6.8.2 从字符串中删除空格 268 6.8.3 计算表达式的值 268 6.8.4 获得项值 270 6.8.5 分析数 271 6.8.6 整合程序 274 6.8.7 扩展程序 275 6.8.8 提取子字符串 277 6.8.9 运行修改过的程序 279 6.9 c++/cli编程 279 6.9.1 理解泛型函数 280 6.9.2 clr版本的计算器程序 285 6.10 小结 290 6.11 练习 291 6.12 本主要内容 292 第7 自定义数据类型 293 7.1 c++中的结构 293 7.1.1 结构的概念 294 7.1.2 定义结构 294 7.1.3 初始化结构 294 7.1.4 访问结构的成员 295 7.1.5 伴随结构的智能感知帮助 298 7.1.6 rect结构 299 7.1.7 使用指针处理结构 300 7.2 数据类型、对象、类和实例 301 7.2.1 类的起源 303 7.2.2 类的操作 303 7.2.3 术语 303 7.3 理解类 304 7.3.1 定义类 304 7.3.2 声明类的对象 305 7.3.3 访问类的数据成员 305 7.3.4 类的成员函数 307 7.3.5 成员函数定义的位置 309 7.3.6 内联函数 309 7.4 类构造函数 310 7.4.1 构造函数的概念 311 7.4.2 默认的构造函数 312 7.4.3 在类定义中指定默认的形参值 314 7.4.4 在构造函数中使用初始化列表 316 7.4.5 声明显式的构造函数 317 7.5 类的私有成员 318 7.5.1 访问私有类成员 320 7.5.2 类的友元函数 321 7.5.3 默认复制构造函数 323 7.6 this指针 325 7.7 类的const对象 327 7.7.1 类的const成员函数 327 7.7.2 类外部的成员函数定义 328 7.8 类对象的数组 329 7.9 类的静态成员 331 7.9.1 类的静态数据成员 331 7.9.2 类的静态函数成员 334 7.10 类对象的指针和引用 334 7.10.1 类对象的指针 334 7.10.2 类对象的引用 337 7.11 c++/cli编程 338 7.11.1 定义值类类型 339 7.11.2 定义引用类类型 344 7.11.3 定义引用类类型的复制构造函数 346 7.11.4 类属性 346 7.11.5 initonly字段 358 7.11.6 静态构造函数 360 7.12 小结 360 7.13 练习 360 7.14 本主要内容 361 第8 深入理解类 363 8.1 类析构函数 363 8.1.1 析构函数的概念 363 8.1.2 默认的析构函数 364 8.1.3 析构函数与动态内存分配 366 8.2 实现复制构造函数 369 8.3 在变量之间共享内存 370 8.3.1 定义联合 371 8.3.2 匿名联合 372 8.3.3 类和结构中的联合 372 8.4 运算符重载 373 …… 第9 类继承和虚函数 第10 标准模板库 第11 调试技术 第12 windows编程的概念 第13 多核编程 第14 使用mfc编写windows程序 第15 处理菜单和工具栏 第16 在窗口中绘图 第17 创建文档和改进视图 第18 使用对话框和控件 第19 存储和打印文档 第20 编写自己的dll

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

madao1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值