C++面向对象——C++高级主题

强制类型转换

static_cast、interpret_cast、const_cast 和 dynamic_cast

static_cast

static_cast用来进行比较“自然”和低风险的转换,比如整型和实数型、字符型之间互相转换。

static_cast不能在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,也不能用于不同类型的引用之间的转换。

static_cast 示例

#include<iostream>
using namespace std;
class A {
public:
	operator int() { return 1; }
	operator char* () { return NULL; }
};
int main() {
	A a;
	int n; 
	const char* p = "New Dragon Inn";
	n = static_cast<int>(3.14); //n的值变为3
	n = static_cast<int>(a); //调用a.operator int,n的值变为1
	p = static_cast<char*>(a); //调用a.operator char*,p的值变为NULL
	n = static_cast<int>(p); //编译错误,static_cast不能将指针转换成整型
	p = static_cast<char*>(n); //编译错误,static_case不能将整型转换成指针
	return 0;
}

reinterpret_cast

reinterpret_cast用来进行各种不同类型的指针之间的转换、不同类型的引用之间的转换、以及指针和能容纳得下指针的整数类型(字节=>4)之间的转换。转换的时候,执行的是逐个比特拷贝的操作。

reinterpret_cast示例

#include<iostream>
using namespace std;
class A {
public:
	int i;
	int j;
	A(int n) :i(n), j(n){ }
};
int main() {
	A a(100);
	int& r = reinterpret_cast<int&>(a); //强行让r引用a
	r = 200; //把a.i变成了200。
	//A是8字节的,r是4字节,所以r引用的是a.i而不是a.j。
	cout << a.i << "," << a.j << endl; //输出 200,100
	int n = 300;
	A* pa = reinterpret_cast<A*>(&n); //强行让pa指向n
	pa->i = 400;    //n变成400
	pa->j = 500; //此条语句不安全,很可能导致程序崩溃
	cout << n << endl; //输出 400
	long long la = 0x12345678abcdLL;
	pa = reinterpret_cast<A*>(la); //la太长,只取低32位0x5678abcd拷贝给pa
	unsigned int u = reinterpret_cast<unsigned int>(pa); //pa逐个比特拷贝到u
	cout << hex << u << endl; //输出 5678abcd
	typedef void(*PF1)(int);
	typedef int(*PF2)(int, char*);
	PF1 pf1;		PF2 pf2;
	pf2 = reinterpret_cast<PF2>(pf1); //两个不同类型的函数指针之间可以互相转换
	return 0;
}
// 输出结果:
// 200, 100
// 400
// 5678abcd

const_cast

用来进行去除const属性的转换。将const引用转换成同类型的非const引用,将const指针转换为同类型的非const指针时用它。例如:

const string s = "Inception";
string& p = const_cast<string&>(s);
string* ps = const_cast<string*>(&s); //&s的类型是const string*

dynamic_cast

  • dynamic_cast专门用于将多态基类的指针或引用,强制转换为派生类的指针或引用,而且能够检查转换的安全性。对于不安全的指针转换,转换结果返回NULL指针。
  • dynamic_cast不能用于将非多态基类的指针或引用,强制转换为派生类的指针或引用

dynamic_cast示例

#include<iostream>
#include<string>
using namespace std;
class Base {
	//有虚函数,因此是多态基类
public:
	virtual~Base(){ }
};
class Derived:public Base{ };
int main() {
	Base b;
	Derived d;
	Derived* pd;
	pd = reinterpret_cast<Derived*>(&b);
	if (pd == NULL) //此处pd不会为NULL。
	//reinterpret_cast不检查安全性,总是进行转换
		cout << "unsafe reinterpret_cast" << endl;
		//不会执行
	pd = dynamic_cast<Derived*>(&b);
	if (pd == NULL)
		//结果会是NULL,因为&b不是指向派生类对象,此转换不安全
		cout << "unsafe dynamic_cast1" << endl; //会执行
	Base* pb = &d;
	pd = dynamic_cast<Derived*>(pb); //安全的转换
	if (pd == NULL) //此处pd不会为NULL
		cout << "unsafe dynamic_cast2" << endl; //不会执行

		return 0;
}
// 输出结果:
// unsafe dynamic_cast1

Derived&r=dynamic_cast<Derived&>(b);
//b是基类的对象,即基类的引用。

那该如何判断该转换是否安全?

答案:不安全则抛出异常

异常处理

– 程序运行中总难免发生错误

  • 数组元素的下标超界、访问NULL指针
  • 除数为0
  • 动态内存分配new需要的存储空间太大

– 引起这些异常情况的原因:

  • 代码质量不高,存在BUG
  • 输入数据不符合要求
  • 程序的算法设计时考虑不周到

– 我们总希望在发生异常情况时

  • 不只是简单地终止程序运行
  • 能够反馈异常情况的信息:哪一段代码发生的、什么异常
  • 能够对程序运行中已发生的事情做些处理:取消对输入文件的改动、释放已经申请的系统资源

– 通常的做法是:在预计会发生异常的地方,加入相应的代码,但这种做法并不总是适用的

 	......//对文件A进行了相关的操作
	fun(arg,...);//可能发生异常
	......
  • caller该如何知道fun(arg,…)是否发生异常
    –没有发生异常,可以继续执行
    –发生异常,应该在结束程序运行前还原对文件A的操作

  • fun(arg,…)是别人已经开发好的代码
    – fun(arg,…)的编写者不知道其他人会如何使用这个函数
    – fun(arg,…)会出现在表达式中,通过返回值的方式区分是否发生异常

    • 不符合编写程序的习惯
    • 可能发生多种异常,通过返回值判断也很麻烦
  • 需要一种手段
    – 把异常与函数的接口分开,并且能够区分不同的异常
    – 在函数体外捕获所发生的异常,并提供更多的异常信息

用try、catch处理异常

#include<iostream>
using namespace std;
int main() {
	double m, n;
	cin >> m >> n;
	try {
		cout << "before dividing." << endl;
		if (n == 0)
			throw - 1; //抛出int类型异常
		else
			cout << m / n << endl;
		cout << "after dividing." << endl;
	}
	catch (double d) {
		cout << "catch(double)" << d << endl;
	}
	catch (int e) {
		cout << "catch(int)" << e << endl;
	}
	cout << "finished" << endl;
	return 0;
}
// 输出:
// 9  6 
// before dividing.
// 1.5
// after dividing.
// finished
//捕获任何异常的catch块
#include<iostream>
using namespace std;
int main() {
	double m, n;
	cin >> m >> n;
	try {
		cout << "before dividing." << endl;
		if (n == 0)
			throw - 1; //抛出整型异常
		else if (m == 0)
			throw - 1.0; //抛出double型异常
		else
			cout << m / n << endl;
		cout << "after dividing." << endl;
	}
	catch (double d) {
		cout << "catch(double)" << d << endl;
	}
	catch (...) { //胜任任何异常
		cout << "catch(...)" << endl;
	}
	cout << "finished" << endl;
	return 0;
}
// 输出:
// 9 0
// before dividing.
// catch (...)
// finished

// 0 6
// before dividing.
// catch (double)-1
// finished

异常的再抛出

如果一个函数在执行的过程中,抛出的异常在本函数内就被catch块捕获并处理了,那么该异常就不会抛给这个函数的调用者(也称“上一层的函数”);如果异常在本函数中没被处理,就会被抛给上一层的函数。

#include<iostream>
#include<string>
using namespace std;
class CException {
public:
	string msg;
	CException(string s) :msg(s){ } //构造函数
};
double Devide(double x, double y) {
	if (y == 0)
		throw CException("devided by zero"); 
	//抛出异常,直接跳到下面的catch(CException e){}处
	cout << "in Devide" << endl;
	return x / y;
}
int CountTax(int salary) {
	try {
		if (salary < 0)
			throw - 1;
		cout << "counting tax" << endl;
	}
	catch (int) {
		cout << "salary<0" << endl;
	}
	cout << "tax counted" << endl;
	return salary * 0.15;
}
int main() {
	double f = 1.2;
	try {
		CountTax(-1);
		f = Devide(3, 0);
		cout << "end of try block" << endl;
	}
	catch (CException e) {
		cout << e.msg << endl;
	}
	cout << "f=" << f << endl; 
	cout << "finished" << endl;
	return 0;
}
// 输出结果:
//	salary < 0
//	tax counted
//	devided by zero
//	f = 1.2
//	finished

C++标准异常类

  • C++标准库中有一些类代表异常,这些类都是从exception类派生而来。常用的几个异常类如下:
    • bad_typeid
    • bad_cast
    • bad_alloc
    • ios_base::failure
    • logic_error ——> out_of_range

bad_cast (类型转换)

在用dynamic_cast进行从多态基类对象(或引用),到派生类的引用的强制类型的转换时,如果转换是不安全的,则会抛出此异常。

#include<iostream>
#include<stdexcept>
#include<typeinfo>
using namespace std;
class Base {
	virtual void func(){ }
};
class Derived :public Base {
public:
	void Print(){ }
};
void PrintObj(Base& b) {
	try {
		Derived& rd = dynamic_cast<Derived&>(b);
		//此转换若不安全,会抛出bad_cast异常
		rd.Print(); //异常时此句会被跳过
	}
	catch (bad_cast & e) {
		cerr << e.what() << endl; //cerr是标准输出
	};
}
int main(){
	Base b;
	PrintObj(b);
	return 0;
}
// 输出:Bad dynamic_cast!

bad_alloc

在用new运算符进行动态内存分配时,如果没有足够的内存,则会引发此异常

#include<iostream>
#include<stdexcept>
using namespace std;
int main() {
	try {
		char* p = new char[0x7fffffff]; //无法分配这么多空间,会抛出异常
	}
	catch (bad_alloc & e) {
		cerr << e.what() << endl;
	}
	return 0;
}
// 输出:bad allocation

out_of_range

用vector或string的at成员函数根据下标访问元素时,如果下标越界,就会抛出此异常。例如:

#include<iostream>
#include<stdexcept>
#include<vector>
#include<string>
using namespace std;
int main() {
	vector<int>v(10);
	try {
		v.at(100) = 100; //抛出out_of_range异常
	}
	catch (out_of_range & e) {
		cerr << e.what() << endl;
	}
	string s = "hello";
	try {
		char c = s.at(5); //抛出out_of_range异常
	}
	catch (out_of_range & e) {
		cerr << e.what() << endl;
	}
	return 0;
}
// 输出:
//	invalid vector<T> subscript
//	invalid string position
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
面向对象的编程中,C语言并不直接支持类和抽象的概念。引用中提到,final关键字用来修饰方法,表示该方法不能在子类中被覆盖。而abstract关键字用来修饰抽象方法,表示该方法必须在子类中被实现。然而,在C语言中,没有对应的关键字来实现类和抽象的概念。 相反,C语言通过结构体来模拟类的概念。结构体是一种用户自定义的数据类型,可以包含多个不同类型的数据成员。通过结构体,我们可以将相关的数据和功能组合在一起。然而,C语言中的结构体不支持继承和多态等面向对象的特性。 在C语言中,我们可以使用函数指针来模拟抽象类和接口的概念。函数指针可以指向不同的函数,通过使用函数指针,我们可以实现多态性,即在运行时根据函数指针指向的具体函数来执行不同的操作。 综上所述,C语言并不直接支持面向对象中的类和抽象的概念,但可以使用结构体和函数指针来实现类似的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [面向对象——类和对象](https://blog.csdn.net/shouyeren_st/article/details/126210622)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *2* [面向对象编程原则(06)——依赖倒转原则](https://blog.csdn.net/lfdfhl/article/details/126673771)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Driver.SHAO

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

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

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

打赏作者

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

抵扣说明:

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

余额充值