68、令人迷惑的写法

令人迷惑的写法1、

  • 下面的程序想要表达什么意思?
    在这里插入图片描述
#include <iostream>
#include <string>
using namespace std;

template <class T>
class Test
{
public:
	Test(T i)
	{
		cout << "Test(T i): " << i << endl;
	}

};

template <class T>
void func(T a[], int length)
{
	for (int i = 0; i < length; i++)
	{
		cout << a[i] << endl;
	}
}

int main()
{
	Test<string> t("xiebs");

	string sa[5] = { "C++","Qt", "Python", "Go", "Java" };
	func(sa, 5);

	Test<int> t1(1);
	int array[3] = { 1,2,3 };
	func(array, 3);

	return 0;
}

在这里插入图片描述
说明 模板里面写成 class 并不是限定只能定义类类型。

  • 历史上的原因
    — 早期的C++直接复用 class 关键字来定义模板
    — 但是泛型编程针对的不只是类类型
    — class 关键字的复用使得代码出现二义性
#include <iostream>
#include <string>

using namespace std;

int a = 0;
class Test1
{
public:
	static const int TS = 1;
};

class Test2
{
public:
	struct TS
	{
		int value;
	};
};

template <class T>
void class_test()
{
	T::TS * a;       //1、通过泛指类型 T 内部的数据类型 TS 定义指针变量 a
					 //2、通过泛指类型 T 内部的静态成员变量 TS 定义指针变量 a
}

int main()
{
	class_test<Test1>();
	class_test<Test2>();

	return 0;
}

在这里插入图片描述
程序报错,为什么会这样呢?因为在第25行的函数里,这条语句其实是具有二义性的。当我们调用第31行的class_test<Test1>();这条语句时,程序不会报错,因为它只代表一种意思,就是静态成员变量和全局变量 a 相乘。但是当我们调用第32行的class_test<Test2>();这条语句时,编译器会不知所措了,他不知道我们到底是要调用定义指针 a 呢还是要他和 0 相乘呢。所以我们得到提示:如果你更倾向于让 TS 作为一个数据类型来使用,你就应该用typename关键字来说明 TS 是一个数据类型而不是一个成员变量

#include <iostream>
#include <string>

using namespace std;

int a = 0;
class Test1
{
public:
	static const int TS = 1;
};

class Test2
{
public:
	struct TS
	{
		int value;
	};
};

template <class T>
void class_test()
{
	typename T::TS * a;       //1、通过泛指类型 T 内部的数据类型 TS 定义指针变量 a
							  //2、通过泛指类型 T 内部的静态成员变量 TS 定义指针变量 a
}

int main()
{
	//class_test<Test1>();
	class_test<Test2>();

	return 0;
}

这样的代码是没有问题的。

  • 所以typename 有两个作用:
    1、在模板定义中声明泛指类型
    2、解决这样的二义性问题,明确告诉编译器,紧跟其后的标识符是一个类型。

令人迷惑的写法2、

  • 下面的程序想要表达什么意思?
    在这里插入图片描述
  • 左边的图片:
    1、 try…catch 用于分隔正常功能代码和异常处理代码
    2、 try…catch 可以直接将函数实现分隔为 2 部分
  • 右边的图片
    1、 函数声明和函数定义时可以直接指定可能抛出的异常类型
    2、 异常声明成为函数的一部分可以提高代码可读性
  • 函数异常声明的注意事项
    — 函数异常声明是一种与编译器之间的契约
    — 函数声明异常后就只能抛出声明的异常
    抛出其他异常将导致程序运行终止
    可以直接通过异常声明定义无异常函数
#include <iostream>
#include <string>

using namespace std;

int func(int x, int y)throw(int)		//函数异常声明
{
	if (y > 0 && y < 10)
	{
		return x + y;
	}
	else
	{
		throw 0;
	}
}

void test(int i)try					  // try...catch 用于分隔正常功能代码和异常处理代码
{
	cout << "func(i,i) = " << func(i, i) << endl;
}
catch(int)
{
	cout << "exception 1" << endl;
}
catch (...)
{
	cout << "exception 2" << endl;
}
int main()
{
	test(5);
	test(10);

	return 0;
}

在这里插入图片描述
如果在函数异常声明哪里最后 throw的类型和异常声明的类型不一样,会出现什么结果呢?
在这里插入图片描述
程序异常终止。因为我们前面说了,函数声明异常后就只能抛出声明的异常,抛出其他异常将导致程序运行终止。

修改方式:

int func(int x, int y)throw(intchar)		//函数异常声明
{
	if (y > 0 && y < 10)
	{
		return x + y;
	}
	else
	{
		throw 0;
	}
}
  • 小结
    1、class 可以用来在模板中定义泛指类型(不推荐)
    2、typename 是可以消除模板中的二义性
    3、try...catch 可以将函数体分为两部分
    4、异常声明能够提供程序的可读性
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值