函数模板
函数模板还可以处理多个通用数据类型。 使用逗号分隔需定义的数据类型。
让我们来创建一个包含多种不同数据类型的参数(一个int和一个double)的函数,然后打印一个较小的值。
template <class T, class U>
正如你所看到的,这个模板声明了两个不同的通用数据类型T和U.
现在我们可以继续我们的函数声明:
template <class T, class U>
T smaller(T a, U b) {
return (a < b ? a : b);
}
(a < b ? a : b)三元运算符用法,检查a<b,如果条件成立返回a,否则返回b
在main中,我们可以使用不同数据类型的函数:
template <class T, class U>
T smaller(T a, U b) {
return (a < b ? a : b);
}
int main () {
int x=70;
double y=69.99;
cout << smaller(x, y) << endl;
}
// 输出 69
最后输出是int型的,应为我们在调用函数模板的时候声明了int型函数。
类模板
就像我们可以定义函数模板一样,我们也可以定义类模板,允许类使用模板参数的成员作为类型。
使用相同的语法来定义类模板:
template <class T>
class MyClass {
};
就像函数模板一样,您可以使用逗号分隔的列表来定义多个通用数据类型。
我们创建一个类Pair,它将保存一对通用类型的值。
template <class T>
class Pair {
private:
T first, second;
public:
Pair (T a, T b):
first(a), second(b) {
}
};
上面的代码声明了一个类模板Pair,带有两个泛型类型的私有变量,以及一个用于初始化变量的构造函数。
如果您在类的外部定义成员函数,则需要使用特定的语法,例如在单独的源文件中。
您需要在类名后面的尖括号中指定泛型类型。
例如,要在类的外部定义一个成员函数bigger(),使用以下语法:
template <class T>
class Pair {
private:
T first, second;
public:
Pair (T a, T b):
first(a), second(b){
}
T bigger();
};
template <class T>
T Pair<T>::bigger() {
// 此处省略
}
定义一个MyClass成员函数hello(),其中MyClass是一个模板类,hello()将输出Hi
template <class T>
void MyClass<T>::hello()
{
cout << "Hi" << endl;
}
bigger()函数将返回两个成员变量的最大值。
template <class T>
class Pair {
private:
T first, second;
public:
Pair (T a, T b):
first(a), second(b){
}
T bigger();
};
template <class T>
T Pair<T>::bigger() {
return (first>second ? first : second);
}
模板类的对象
要创建不同类型的模板类的对象,请在尖括号中指定数据类型,就像我们在定义类之外的函数时所做的那样。
在这里,我们为整数创建一个Pair对象。
Pair <int> obj(11, 22);
cout << obj.bigger();
// 输出 22
我们也可以使用相同的类来定义一个double型的对象
Pair <double> obj(23.43, 5.68);
cout << obj.bigger();
// 输出 23.43
异常处理
处理用户输入操作时,异常处理特别有用。
例如,一个要求用户输入两个数字的程序,然后进行整除,你必须对0进行限制,防止用户在第二个数字的时候输入0。
int main() {
int num1;
cout <<"输入你的第一个数字";
cin >> num1;
int num2;
cout <<"输入你的第二个数字";
cin >> num2;
cout <<"Result:"<<num1 / num2;
}
如果用户输入除0之外的任何数字,则该程序正常工作。
但是如果输入了0则会导致程序崩溃,所以我们需要对输入的数据进行处理。
模板特化
要为数据类型char指定不同的行为,我们将创建一个模板特化。
template <class T>
class MyClass {
public:
MyClass (T x) {
cout <<x<<"不是char类型"<<endl;
}
};
template < >
class MyClass<char> {
public:
MyClass (char x) {
cout <<x<<"是char类型!"<<endl;
}
};
首先,请注意,我们在模板<>的前面添加了一个空的参数列表。 这是因为所有类型都是已知的,并且这个特化不需要模板参数,但是仍然是类模板的特化,因此需要注意这一点。
但比这个前缀更重要的是类模板名称之后的char特化参数。 这个特化参数本身标识了模板类被特化的类型(char)。
在上面的例子中,第一个类是通用模板,第二个是特化。
下一步是声明不同类型的对象并检查结果:
int main () {
MyClass<int> ob1(28);
MyClass<double> ob2(5.18);
MyClass<char> ob3('w3cschool');
}
/* 输出:
28 - 不是char类型
5.18 - 不是char类型
w3cschool 是char类型!
*/
正如你所看到的,泛型模板为int和double所调用。 但是,我们的模板特化是为char数据类型调用的。
请记住,从泛型模板到特化没有成员“继承”,所以模板类特化的所有成员都必须自行定义。
异常
程序执行过程中出现的问题称为异常。
在C ++中,异常是程序运行时产生的错误的反应,例如试图除以零。
C ++异常处理基于三个关键字:try,catch和throw。
当问题出现时throw是用来抛出异常的。
int fatherAge = 18;
int sonAge = 38;
if (sonAge > fatherAge) {
throw "你爸真年轻~";
}
上述代码中,当sonAge比fatherAge大的时候就会抛出异常。
在throw语句中,参数确定异常的类型。 这可以是任何表达式。 表达式结果的类型将决定抛出异常的类型。
捕捉异常
try标识将激活用于检测异常的代码块。 接下来是一个或多个catch块。 catch关键字表示在引发特定异常时执行的代码块。
可以生成异常的代码被try / catch块包围。
您可以通过设置关键字catch后面的括号中显示的异常声明来捕获哪种类型的异常。
try {
int fatherAge = 18;
int sonAge = 38;
if (sonAge > fatherAge) {
throw 666;
}
}
catch (int x) {
cout<<"你确定这是你爸?"<<x;
}
//输出 "你确定这是你爸? 666"
try块引发异常,然后catch块处理它。
错误代码666是一个整数,它出现在throw语句中,所以它会导致int类型的异常。
可能会列出多个catch语句来处理各种异常,以防try块引发多个异常。
异常处理
处理用户输入时,异常处理特别有用。
例如,一个程序要求用户输入两个数字,然后进行整除,为了确保被除数不为0,你就需要用异常处理。
int main() {
int num1;
cout <<"输入第一个数字:";
cin >> num1;
int num2;
cout <<"输入第二个数字:";
cin >> num2;
cout <<"结果:"<<num1 / num2;
}
如果用户输入除0外的任何数字,程序将正常运行。
但是当用户在第二个数字的时候输入了0,则程序会崩溃
如果第二个数字等于0,我们需要抛出异常。
int main() {
int num1;
cout <<"输入第一个数字:";
cin >> num1;
int num2;
cout <<"输入第二个数字:";
cin >> num2;
if(num2 == 0) {
throw 0;
}
cout <<"结果:"<<num1 / num2;
}
如果第二个数字输入为0,这段代码将会抛出一个int型的异常:0。
现在我们需要使用try / catch块来处理抛出的异常。
int main() {
try {
int num1;
cout <<"输入第一个数字:";
cin >> num1;
int num2;
cout <<"输入第二个数字:";
cin >> num2;
if(num2 == 0) {
throw 0;
}
cout <<"结果:"<<num1 / num2;
}
catch(int x) {
cout <<"请勿输入0!";
}
}
上述代码将会避免出现当num2为0时导致程序崩溃的问题,同时会抛出异常。
在我们的例子中,我们只捕获整数类型的异常。 可以指定你的catch块处理在try块中抛出的任何类型的异常。可以通过在catch的括号之间添加一个省略号(...)实现。
try {
// 要执行的代码
} catch(...) {
// 用来处理异常的代码
}
引入文件
另一个有用的C++功能是读取和写入文件的能力。这需要标准的C++库fstream。
fstream中定义了三种新的数据类型:
ofstream:输出文件流,创建信息并将其写入文件。
ifstream:输入从文件中读取信息的文件流。
fstream:一般文件流,具有ofstream和ifstream功能,允许它创建,读取和写入信息到文件。
要在C++中执行文件处理,头文件iostream和fstream必须包含在C ++源文件中。
#include <iostream>
#include <fstream>
打开文件
必须先打开一个文件,然后才能读取或写入文件。
可以使用ofstream或fstream对象来打开文件进行写入。
我们打开一个名为“w3cschool.txt”的文件,并写下一些内容:
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream MyFile;
MyFile.open("w3cschool.txt");
MyFile << "hellow W3cSchool";
}
上面的代码创建一个名为MyFile的流对象,并使用open()函数在文件系统上打开“w3cschool.txt”文件。 如您所见,使用相同的流输出操作符来写入文件。
如果指定的文件不存在,打开的功能会自动创建。
关闭文件
当你完成对一个文件的操作后,可以使用成员函数close()关闭它。
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream MyFile;
MyFile.open("w3cschool.txt");
MyFile << "hellow W3cSchool";
MyFile.close();
}
引入文件
您也可以使用ofstream对象的构造函数直接提供文件的路径并打开,而不是调用open函数。
#include <fstream>
using namespace std;
int main() {
ofstream MyFile("w3cschool.txt");
MyFile << "哇!666666";
MyFile.close();
}
在某些情况下,例如当您没有文件权限时,可能会打开失败。
is_open()成员函数检查文件是否可以被访问。
#include <iostream>
#include <fstream>
using namespace std;
int main() {
ofstream MyFile("w3cschool.txt");
if (MyFile.is_open()) {
MyFile << "hellow W3cschool";
}
else {
cout << "抱歉,文件无法访问";
}
MyFile.close();
}
文件打开模式
可以通过设置open函数的第二个参数定义打开文件的模式。
可以通过"|"设定多个模式。
例如,要以写入模式打开文件并截断它(如果已经存在),请使用以下语法:
ofstream outfile;
outfile.open("file.dat", ios::out | ios::trunc );
从文件读取
您可以使用ifstream或fstream对象从文件读取信息。
#include <iostream>
#include <fstream>
using namespace std;
int main () {
string line;
ifstream MyFile("w3cschool.txt");
while ( getline (MyFile, line) ) {
cout << line << '\n';
}
MyFile.close();
}
getline函数从输入流中读取字符并将其放入字符串中。