news 2026/9/3 1:33:39

C语言对话-27.Baseless Exceptions

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言对话-27.Baseless Exceptions

WQ翻译

“嗯,不错,不错,”我自我感觉良好。“看起来我发现了一个编译器错误。”

我正在追踪一个故障,有一个异常没有被异常处理体系捕获。精简后的代码如下:

class Base

{

// ...

whatever ...

};

class Derived : private Base

{

void f() {

try {

throw Derived();

}

catch (Base &) {

cout << "Caught Base" << endl;

}

catch (...)

{

cout << "Caught something else" << endl;

}

}

};

我预期程序会隐式地将Derived对象转换为Base &,因此会打印出 "捕获Base"。然而,实际上,它没有转换而打印出 "Caught something else"。沾沾自喜于发现了编译器的错误时,我记起了Guru的教导:“记住,徒弟,绝不要假设你或编译器中的任何一个是正确的。总是查神圣的标准来确定你的理解和智慧。”几分钟之后,我的自喜已转变为迷惑,因为标准说编译器的行为是正确的:

一个捕获程序匹配于抛出的类型E的对象,条件是……捕获程序的参数类型是cv T或cv T&并且T是E的一个无二义公有基类……[1]

无二义的公有基类?好吧,这有些不合理,我认为。毕竟,那个Derived被转换为Base对象,因为它处在自己的代码空间中。为了测试这个问题,我在try语句前增加了一句语句

Base & testRef = *this;

肯定,它没有任何编译问题。

“你看起来很迷惑,徒弟。”我轻轻抖了一下-我不认为我会习惯于Guru的突然出现。

“嗯,是的,”我嘟囔着。“我不理解标准为什么不允许异常被类型转换,而在代码中又是允许的。”

她在捧着的“砖头”上小心地作记号了然后合上。“在语句'Base & testRef = *this;'上发生的类型转换,编译器什么时候检查转换是否允许进行,以及取决于存取权限还是继承?”

“嗯,当它构造这个引用的时候,它进行检查。不,等等-说错了。它在编译的时候。”

“正确,孩子。接下来,编译器什么时候为异常捕获程序要求的类型转换作检查?”

“它也发生于编译期,不是吗?”Guru没回答。实际上,她是扬起眉毛,用灰色的眼睛盯着我-这是她的“你确定吗?”。我死命想了一会儿。然后明白了。“哦,当然,”我说。“评估发生在运行期。任何类型的对象都可以被抛出,所以异常捕获程序必须查看实际对象的类型。”

“而访问权限信息在运行期存放在哪里?”Guru提示道。

“它被储存于……”我停了一下。“嗨,这有个陷阱-访问检查应该在编译期进行,不会存储任何运行期信息。啊,我现在明白了-这里存在限制是因为缺乏访问权限信息。”

我以为Guru会于此时离开。然而,她没有。她只是静静地站在我的隔间旁边。我又想了一下。

“等一下,”我慢慢地说。“不,我还是没明白。程序仍然必须知道,是否存在一个无二义的公有基类以进行转换。这会发生什么……”我转向白板,写了个比较复杂的例子:

#include

using namespace std;

class Base

{

public:

virtual ~Base(){}

};

class DerivedPrivately : private Base

{

// ...

whatever ...

};

class DerivedPublicly : public Base

{

// ...

whatever ...

};

void g(int which)

{

if (which < 0) throw DerivedPublicly();

else if (which == 0) throw Base();

else throw DerivedPrivately();

}

void f(int value)

{

try {

g(value);

}

catch(Base &)

{

cout << "Caught Base" << endl;

}

catch ( ... )

{

cout << "Caught something else" << endl;

}

}

int main()

{

f(-1);

f(0);

f(1);

}

“如果我正确地理解了标准,前两个调用的输出应该是'Caught Base',最后一个是'Caught something else'。编译器显然做了某种运行期检查,所以限定它为公有无二义基类?”

“啊,孩子,如果你修改私有派生出的类,加一句friend void f(int);语句。然后你预料会发生什么?”

“ 嗯, DerivedPrivately对象已经申明f()为友元,所以我预料它打印出'Caught Base'。”

她微笑了一下。“现在,程序需要什么信息以知道类型转换是可接受的?”

我曾经提到Guru的Socratic方法是多么令人苦恼吗?我思索了一下。“嗯,”我冒险一试,“显然,它必须跟踪继承树。而且,我猜测它也必须跟踪友元。这个应该实现起来很容易,虽然-只要在f()的obj文件中生成一些有序表。”

“啊,但如果f()不知道它是DerivedPrivately友元时怎么办?也就是说,f()所在的编译单元只引用了Base,它根本没有包含申明DerivedPrivately的头文件。不,我的孩子,信息必须全局存储,而且程序需要某种方式将特定的机器码映射到特定的函数,以判决某函数是否作为友元在执行。在复杂体系层次中,可能有任意的友元或隐含类型转换顺序,维护这些信息会是一项重大开销,而还可能并不必要。维护一个无二义的公有基类列表就简单多了。”

“顺便一提,你会注意到,同样的限制也适用于捕获指针的情况。编译器只将会一个指针转换指向它所指类型的无二义公有基类类型的指针。指针确实有额外的能力执行受限的转换。”

Guru转身离开了。“解决办法是明摆着的了,徒弟。顺便提一句,神圣的标准要求进行运行期访问权限的检查的地方仅有两处,异常捕获是其中之一。另外一处当然是……”但是她转了弯,于是我就没听清余下的部分。

当然,Guru是正确的。解决办法很简单:为Derived增加一个异常捕获:

class Derived : private Base

{

void f() {

try {

throw Derived();

}

catch (Derived &) {

cout << "Caught Derived" << endl;

}

catch (Base &) {

cout << "Caught Base" << endl;

}

catch (...) {

cout << "Caught something else" << endl;

}

}

};

[注释]

[1] ISO C++ Standard, clause 15.3 paragraph 3.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/2 23:04:39

着色器变量

在WebGL/GLSL的渲染流水线中&#xff0c;顶点着色器与片元着色器的传参有着严格的规范&#xff0c;这是由GPU的渲染架构和数据处理逻辑决定的&#xff0c;以下是核心规定和原因解析&#xff1a;一、着色器变量的作用域与类型约束 WebGL将着色器变量分为三类&#xff0c;各自有明…

作者头像 李华
网站建设 2026/8/28 19:07:16

AI大模型高薪职位全攻略+学习资料包,助程序员薪资翻倍,从入门到实战_AI大模型岗位薪资揭秘:2026大模型岗位薪资

文章详述AI大模型领域10个高薪职位(薪资60-200万/年)及其要求、发展前景&#xff0c;分析AI人才缺口达400万的就业机遇&#xff0c;提供包含学习路线图、视频教程、面试题等的大模型学习资料包&#xff0c;适合应届生、零基础转型者和传统开发者提升技能&#xff0c;实现职业跃…

作者头像 李华
网站建设 2026/8/28 12:07:51

P7930 [COCI 2021/2022 #1] Set题解

P7930 [COCI 2021/2022 #1] Set 题目背景 在知名游戏 SET 中&#xff0c;存在着一些数字、形状、颜色等不同的卡片&#xff0c;玩家的目标是确定一个存在的 triplet of cards&#xff08;即卡片的三元组&#xff0c;也就是三张卡片构成的组合&#xff09;&#xff0c;使其符合…

作者头像 李华
网站建设 2026/9/2 22:35:39

开发易掌握的知识:GeoHash查找附近空闲车辆

Redis 的 GeoHash 功能提供了一种高效的地理位置数据存储和查询解决方案&#xff0c;以下是其主要应用场景分析&#xff1a; 1. 附近位置查询 典型应用&#xff1a;查找附近的餐厅、酒店、加油站等实现方式&#xff1a;使用 GEORADIUS 或 GEORADIUSBYMEMBER 命令优势&#xf…

作者头像 李华
网站建设 2026/9/3 0:24:51

Excel 大表格复制未响应解决方法参考

Excel如果表格内公式或者内容多的时候复制可能卡死&#xff0c;需要考虑加载项、中英文输入法和定位-对象这几个方面的原因&#xff0c;如果都做了排除还是发现复制卡死&#xff0c;可以考虑是不是文档格式不兼容问题&#xff08;如xls文件内的表格复制到xlsx文件中&#xff09…

作者头像 李华
网站建设 2026/8/31 9:10:11

Claude Code CLI 接入Kimi K2.5模型

用 Claude Code 的交互体验 国产大模型的低成本与低延迟&#xff0c;打造最佳编程工作流。 目录 安装 Claude Code CLI 1. 安装 Node.js 2. 通过 npm 安装 Claude Code 3. 验证安装 核心原理 方案一&#xff1a;接入 Kimi K2.5&#xff08;Moonshot&#xff09; 1. 获取…

作者头像 李华