C++学习总结(2021/06/20)

模板的使用

1、函数模板

1.1 定义

不同类型的数据使用的方法内部实现一致,得写遍相同方法,使用函数模板就只要写一次方法,传入不同的类型.

定义方式:
template <类型形式参数表>
类型 函数名(参数) {// ……}

template <typename T1, typename T2>
T1 max(T1 a, T2 b) {
   return a>b ? a : b;
}

int a=10;
int b=20;
char c=‘M’;
//调用模板函数
max(a,b);
max(a,c);

1.2 函数模板与普通函数

① 当函数模板和普通函数都存在时,若与普通函数更匹配则优先调用普通函数,当没有普通函数可以匹配时会调用函数模板

[例] 代码:

template <typename T1, typename T2>
void swap(T1 a, T2 b){
	T1 temp = a;
	a = b;
	b = temp;
}

void swap(char a, char b){
	char temp = a;
	a = b;
	b = temp;
}

int main(){
	char a = 'q';
	char b = 'e';
	int c = 10;
	int d= 20;

	swap(a,b);		//调用普通函数void swap(char a, char b)
	swap(a,c);		//调用函数模板void swap(T1 a, T2 b)
	swap(c,d);		//调用函数模板void swap(T1 a, T2 b)
	
	return 0;
}

② 当只有函数模板时,不会进行类型的隐式转换

[例] 代码:

template <typename T>
void swap(T a, T b){
	T temp = a;
	a = b;
	b = temp;
}

int main(){
	char a = 'q';
	char b = 'e';
	int c = 10;
	int d= 20;

	swap(a,c);		//不会隐式类型转换,会报错
	
	return 0;
}

函数模板与普通函数的异同:
1⃣️ 函数模板可以与普通函数共存
2⃣️ 函数模板不允许自动类型转换,普通函数可以

2、类模板

2.1 定义

与函数模板类似,当存在多个类,其内部方法实现的功能都类似,只是数据类型不同

定义方式:
template <类型形式参数表>
class 类名{
// …
};

2.2 类模板的三种描述方式

2.2.1 所有的类模板函数写在类的内部

这种方式需要在创建对象时,将类模板中的虚拟类型实例化

[例] 代码:

template <typename T>
class A{
public:
	A(T data){
		this->data = data;
	}
	T getData(){
		return data;
	}

private:
	T data;
};

int main(){
	A<int> a(10);
	std::cout << a.getData() << std::endl;

	return 0;
}

2.2.2 所有的类模板函数写在类的外部,在一个cpp中

将类模板中的方法定义与方法实现都放在一个cpp文件中,但进行分开,然后创建并调用类模板中的方法。这种方式在进行方法的实现时需要将表示类模板的template <typename T>与类的模板类型也写出来

[例] 代码:

template <typename T>
class A{
public:
	A(T data);
	T getData();
	A operator+ (const A &a);
	
private:
	T data;
};

template <typename T>
A<T>::A(T data){
	this->data = data;
}

template <typename T>
T A<T>::getData(){
	return data;
}

template <typename T>
A<T> A<T>::operator+ (const A<T> &a){
	return A<T>(data+a.data);
}

int main(){
	A<int> cla(666), cla2(888);
	cout << (cla + cla2).getData() << endl;
	
	return 0;
}
2.2.2 所有的类模板函数写在类的外部,在不同的.h和.cpp中

这种方式只需包含类中方法的实现文件就可以进行使用

[例] A.h 代码:

#pragma once

template <typename T>
class A{
public:
	A(T data);
	T getData();
	A operator+ (const A &a);
	
private:
	T data;
};

[例] A.hpp 代码:

#include "A.h"

template <typename T>
A<T>::A(T data){
	this->data = data;
}

template <typename T>
T A<T>::getData(){
	return data;
}

template <typename T>
A<T> A<T>::operator+ (const A<T> &a){
	return A<T>(data+a.data);
}

[例] main.cpp 代码:

#include <iostream>
#include "mode.hpp"

int main() {
	A<int> cla(666), cla2(888);
	std::cout << (cla + cla2).getA() << std::endl;
	
	return 0;
}

2.3 类模板中的友元函数

使用友元函数时需要在声明为友元函数前标明为模板类,在友元函数的实现前也同样需标明

[例] 代码:

#include <iostream>

using namespace std;

template <typename T>
class A{
public:
	A(T data);
	T getData();
	A operator+ (const A &a);
	
	template <typename T>
	friend ostream &operator<< (ostream &os, const A<T> &a);
	
private:
	T data;
};

template <typename T>
A<T>::A(T data){
	this->data = data;
}

template <typename T>
T A<T>::getData(){
	return data;
}

template <typename T>
A<T> A<T>::operator+ (const A<T> &a){
	return A<T>(data+a.data);
}

template <typename T>
ostream &operator<< (ostream &os, const A<T> &a){
	os << "A中data: " << a.data;
	
	return os;
}

int main(){
	A<int> cla(666), cla2(888);
	cout << cla + cla2 << endl;
	
	return 0;
}

2.4 类模板中的静态成员

[例] 代码:

在静态成员初始化前需要标明类模板 template <typename T>,改变静态成员的值后,其他对象调用静态成员也是改变后的值

#include <iostream>

using namespace std;

template <typename T>
class A{
public:
	A(T data);
	T getData();
	A operator+ (const A &a);
	friend ostream &operator<< (ostream &os, const A<T> &a);
	
public:
	static int count;
private:
	T data;
};

template<typename T>
int A<T>::count = 999;

template <typename T>
A<T>::A(T data){
	this->data = data;
}

template <typename T>
T A<T>::getData(){
	return data;
}

template <typename T>
A<T> A<T>::operator+ (const A<T> &a){
	return A<T>(data+a.data);
}

template <typename T>
ostream &operator<< (ostream &os, const A<T> &a){
	os << "A中data: " << a.data;
	
	return os;
}

int main(){
	A<int> cla(666), cla2(888);
	cla.count = 888;
	cout << "cla.count: " << cla.count << endl;
	cout << "cla2.count: " << cla2.count << endl;
	
	return 0;
}

3、异常处理机制

3.1 传统错误处理机制

不使用异常处理机制监测错误,一般通过返回值来抛出不一样的报错信息

[例] 代码(实现文件的拷贝):

#include <stdio.h>
#include <stdlib.h>

int copyFile(const char *src, const char *dest){
	FILE *file1 = NULL, *file2 = NULL;
	fopen_s(&file1, src, "rb");	//以只读方式打开二进制文件
	
	if(file1 == NULL){
		return -1;
	}
	
	fopen_s(&file2, dest , "wb");	//以写的方式打开二进制文件
	if(file2 == NULL){
		return -2;
	}
	
	char buffer[1024];	//定义一个数组用来作文件读出与写入的媒介
	int readlen = 0, wirtelen = 0;	//定义变量来存下读与写文件的记录数,判断读与写是否一致

	//把file1读取的数据放到buffer中,直到文件数据都读完
	while((readlen = fread(buffer, 1, 1024, file1)) > 0){
		//把buffer中的数据写到file2中
		writelen = fwrite(buffer, 1, 1024, file2);

		//如果读的数据量和写的数据量不一致,说明有问题
		if(readlen != writelen){
			return -3;
		}
	}
	fclose(file1);
	fclose(file2);

	return 0;
}

int main(){
	int ret = 0;
	ret = copyFile("c:/test/src.txt", "c:/test/dest.txt");
	
	if(ret == -1){
		printf("打开源文件失败!\n");
	}else if(ret == -2){
		printf("打开目标文件失败!\n");
	}else if(ret == -3){
		printf("拷贝文件失败!\n");
	}else{
		printf("未知问题!\n");
	}

	return 0;
}

3.2 C++错误处理机制

方法使用throw进行返回,在调用处用:
try{调用方法} catch(类型 变量名){打印报错信息}

若catch没有抓到匹配的异常,系统会调用abort终止程序

[例] 代码:

#include <stdio.h>
#include <stdlib.h>
#include <string>

int copyFile(const char *src, const char *dest){
	FILE *file1 = NULL, *file2 = NULL;
	fopen_s(&file1, src, "rb");	//以只读方式打开二进制文件
	
	if(file1 == NULL){
		throw new std::string("打开源文件失败!");
	}
	
	fopen_s(&file2, dest , "wb");	//以写的方式打开二进制文件
	if(file2 == NULL){
		throw -2;
	}
	
	char buffer[1024];	//定义一个数组用来作文件读出与写入的媒介
	int readlen = 0, writelen = 0;	//定义变量来存下读与写文件的记录数,判断读与写是否一致

	//把file1读取的数据放到buffer中,直到文件数据都读完
	while((readlen = fread(buffer, 1, 1024, file1)) > 0){
		//把buffer中的数据写到file2中
		writelen = fwrite(buffer, 1, 1024, file2);

		//如果读的数据量和写的数据量不一致,说明有问题
		if(readlen != writelen){
			throw -1.0f;
		}
	}
	fclose(file1);
	fclose(file2);

	return 0;
}

int main(){
	int ret = 0;

	try{
		ret = copyFile("c:/test/src.txt", "c:/test/dest.txt");
	}catch(int error){
		printf("出现异常了,打开目标文件失败!\n");
	}catch(std::string *error){
		printf("出现的问题了,%s\n",error->c_str());
		delete error;
	}catch(float error){	//...表示可以抓到任何类型的异常
		printf("出现异常了,拷贝文件失败!\n");
	}catch(...){	//...表示可以抓到任何类型的异常
		printf("未知异常!\n");
	}

	return 0;
}

3.3 异常接口声明

可以在函数后面加上抛出的异常类型,便于程序可读

int copyFile(const char *src, const char *dest) throw(int, float, string*)

如果不声明抛出的异常类型,则可以抛出任何类型的异常;
声明抛出的异常类型,再抛出其他异常类型,可能会导致程序终止;
当不想要抛出任何异常时,直接声明为 throw()

3.4 异常类型

3.4.1 throw基本类型

与函数return传值一样,函数执行结束,throw的值拷贝到catch中的变量,同时释放throw的变量

[例] 代码:

#include <stdio.h>
#include <stdlib.h>
#include <string>

int copyFile(const char *src, const char *dest){
	FILE *file1 = NULL, *file2 = NULL;
	fopen_s(&file1, src, "rb");	//以只读方式打开二进制文件
	
	if(file1 == NULL){
		char err = 'e';
		throw err;	//抛出基本类型,char类型数据
	}
	
	fopen_s(&file2, dest , "wb");	//以写的方式打开二进制文件
	if(file2 == NULL){
		throw -2;
	}
	
	char buffer[1024];	//定义一个数组用来作文件读出与写入的媒介
	int readlen = 0, writelen = 0;	//定义变量来存下读与写文件的记录数,判断读与写是否一致

	//把file1读取的数据放到buffer中,直到文件数据都读完
	while((readlen = fread(buffer, 1, 1024, file1)) > 0){
		//把buffer中的数据写到file2中
		writelen = fwrite(buffer, 1, 1024, file2);

		//如果读的数据量和写的数据量不一致,说明有问题
		if(readlen != writelen){
			throw -1.0f;
		}
	}
	fclose(file1);
	fclose(file2);

	return 0;
}

int main(){
	int ret = 0;

	try{
		ret = copyFile("c:/test/src.txt", "c:/test/dest.txt");
	}catch(int error){
		printf("出现异常了,打开目标文件失败!\n");
	}catch(char error){
		printf("出现异常了,打开源文件失败!\n");
		delete error;
	}catch(float error){
		printf("出现异常了,拷贝文件失败!\n");
	}

	return 0;
}
3.4.2 throw字符串类型

throw的内容为const char * 类型变量,在catch中抓取,类型必须一致,使用指针,没有值的拷贝,都是指向相同的地址

[例] 代码:

#include <stdio.h>
#include <stdlib.h>
#include <string>

int copyFile(const char *src, const char *dest){
	FILE *file1 = NULL, *file2 = NULL;
	fopen_s(&file1, src, "rb");	//以只读方式打开二进制文件
	
	if(file1 == NULL){
		const char *err = "打开源文件失败";
		printf("抛出异常前,地址:%p",err);
		throw err;	//抛出字符串类型,const char*类型数据
	}
	
	fopen_s(&file2, dest , "wb");	//以写的方式打开二进制文件
	if(file2 == NULL){
		throw -2;
	}
	
	char buffer[1024];	//定义一个数组用来作文件读出与写入的媒介
	int readlen = 0, writelen = 0;	//定义变量来存下读与写文件的记录数,判断读与写是否一致

	//把file1读取的数据放到buffer中,直到文件数据都读完
	while((readlen = fread(buffer, 1, 1024, file1)) > 0){
		//把buffer中的数据写到file2中
		writelen = fwrite(buffer, 1, 1024, file2);

		//如果读的数据量和写的数据量不一致,说明有问题
		if(readlen != writelen){
			throw -1.0f;
		}
	}
	fclose(file1);
	fclose(file2);

	return 0;
}

int main(){
	int ret = 0;

	try{
		ret = copyFile("c:/test/src.txt", "c:/test/dest.txt");
	}catch(int error){
		printf("出现异常了,打开目标文件失败!\n");
	}catch(const char *error){
		printf("出现异常了,%s 地址:%p\n",error,error);
	}catch(float error){
		printf("出现异常了,拷贝文件失败!\n");
	}

	return 0;
}
3.4.3 throw类对象

当抛出类对象时,catch中使用类对象来接,会调用拷贝构造函数;使用引用/指针来接,不会调用拷贝构造函数

[例] 代码:

#include <stdio.h>
#include <stdlib.h>
#include <string>

class Exception{
public:
	Exception(){
		id = 0;
		printf("调用构造函数\n");
	}
	~Exception(){
		printf("调用析构函数\n");
	}
	Exception(const Exception &error){
		id = 1;
		printf("调用拷贝构造函数\n");
	}
	int id;
};

int copyFile(const char *src, const char *dest){
	FILE *file1 = NULL, *file2 = NULL;
	fopen_s(&file1, src, "rb");	//以只读方式打开二进制文件
	
	if(file1 == NULL){
		
		throw Exception();	//抛出类对象
	//	throw  new Exception();
	}
	
	fopen_s(&file2, dest , "wb");	//以写的方式打开二进制文件
	if(file2 == NULL){
		throw -2;
	}
	
	char buffer[1024];	//定义一个数组用来作文件读出与写入的媒介
	int readlen = 0, writelen = 0;	//定义变量来存下读与写文件的记录数,判断读与写是否一致

	//把file1读取的数据放到buffer中,直到文件数据都读完
	while((readlen = fread(buffer, 1, 1024, file1)) > 0){
		//把buffer中的数据写到file2中
		writelen = fwrite(buffer, 1, 1024, file2);

		//如果读的数据量和写的数据量不一致,说明有问题
		if(readlen != writelen){
			throw -1.0f;
		}
	}
	fclose(file1);
	fclose(file2);

	return 0;
}

int main(){
	int ret = 0;

	try{
		ret = copyFile("c:/test/src.txt", "c:/test/dest.txt");
	}catch(Exception error){
		printf("出现异常了,id=%d!\n", error.id);
	}catch(Exception &error){
		printf("出现异常了,id=%d!\n", error.id);
	}catch(Exception *error){
		printf("出现异常了,id=%d!\n", error->id);
	}catch(...){	//...表示可以抓到任何类型的异常
		printf("未知异常!\n");
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值