返回顶部
分享到

我为什么放弃 C++,选择 C 语言编写个人项目?

互联网资讯 2022-8-25 14:25 502人浏览 0人回复
摘要

如果你的目标是保证不会出现未定义的行为,那么C++的异常处理就会变成一场噩梦。由于C++解耦了异常的发生与处理,因此错误处理非常容易,但也造成了你几乎不可能保证程序永远不会运行未定义的行为。 ...

我为什么放弃 C++,选择 C 语言编写个人项目?



首先声明,在整个职业生涯中,我一直在使用C++,而且在做大多数项目时,C++仍然是我的首选语言。

因此,在开始构建个人项目ZeroMQ(可伸缩的分布式或并发应用程序设计的高性能异步消息库)时,我也选用了C++,主要原因如下:

  1. C++包含一些数据结构和算法库。如果使用C语言,我将不得不依赖第三方库,或者自己动手编写基本算法。

  2. C++会强制我在编程风格上保持一些基本的统一性。例如,this参数不允许使用几种不同的机制将指针传递给正在处理的对象,而这个问题在C项目中很常见。同样,不可以明确将成员变量标记为私有,此外还有C++的一些其他特征。

  3. 使用C语言实现虚函数非常复杂,会导致理解和管理代码的难度加剧。不过,严格来说,这个问题其实是上一个问题的一个子集,但我觉得有必要单独指出。

  4. 最后,每个人都喜欢在代码的末尾自动调用析构函数。

然而,事到如今,我不得不承认C++是一个糟糕的选择。下面,我来解释一下原因。

首先,我的个人项目ZeroMQ是一个持续运行的基础设施,永远不应该出故障,永远不应该表现出未定义的行为。因此,错误处理至关重要,必须做到明确且严格。

然而,C++的异常处理并不能满足我的需求。如果程序不会出错,那么选择C++没有任何问题,只需将main函数包装在try/catch中,集中在一个地方处理所有错误。

如果你的目标是保证不会出现未定义的行为,那么C++的异常处理就会变成一场噩梦。由于C++解耦了异常的发生与处理,因此错误处理非常容易,但也造成了你几乎不可能保证程序永远不会运行未定义的行为。

在C语言中,错误的产生和处理是紧密结合的,在同一块源代码中。因此,在出错时很容易理解发生了什么:

int rc = fx ();if (rc != 0)handle_error ();

而在C++中,你只能抛出错误,却不清楚究竟发生了什么:

int rc = fx ();if (rc != 0)throw std::exception ();

问题在于,你并不清楚在哪里处理异常。处理错误的代码在同一个函数中会更加方便理解,尽管不太方便阅读:

try {...int rc = fx ();if (rc != 0)throw std::exception ("Error!");...catch (std::exception &e) {handle_exception ();}

然而,我们来考虑同一个函数抛出两个不同的错误,结果会怎么样:

class exception1 {};class exception2 {};try {...if (condition1)throw my_exception1 ();...if (condition2)throw my_exception2 ();...}catch (my_exception1 &e) {handle_exception1 ();}catch (my_exception2 &e) {handle_exception2 ();}

以下是等效的C代码:

...if (condition1)handle_exception1 ();...if (condition2)handle_exception2 ();...

相较之下,C语言更加方便阅读,而且编译器也会生成更高效的代码。

然而,C++的问题还不仅限于此。考虑某个函数会引发异常,但不会处理异常的情况。在这种情况下,错误的处理可以放到任何地方,具体取决于从哪里调用该函数。

针对不同的情况,采用不同的方式处理异常?这种方法听起来似乎很有道理,但很快就会变成一场噩梦。

在修复某个Bug时,你会发现许多其他地方也有相同的Bug,因为它们都复制了同一段错误处理代码。每当添加一个函数调用,就有可能增加一个新异常,如果调用函数的代码没有妥善处理该异常,就意味着增加了一个新Bug。

如果你还想坚持“没有未定义的行为”原则,就不得不引入新异常,以便区分不同的故障模式。但是,添加新异常就意味着,它会上升到不同的地方。你必须在所有地方添加相应的异常处理,否则就会出现未定义的行为。

看到这里,你可能想说:这就是异常的正确用法啊?

然而问题在于,异常只是一个工具,目的是用更系统的方式管理呈现指数增长的错误处理代码,但它并不能解决根本的问题。甚至可以说,异常有可能导致情况恶化,因为你不仅需要编写新的异常类型,还需要针对新类型编写异常处理代码。

考虑到上述问题,我决定使用C++,但不使用异常。如今我的这个项目就是这样实现的。

不幸的是,问题并没有就此止步……

考虑一下,如果对象的初始化失败,会发生什么?构造函数没有返回值,因此只能通过抛出异常来报告失败。但是,我决定不使用异常。所以,我们必须像下面这样处理:

本文暂无评论,快来抢沙发!

热门问答
达内教育:成立于2002年。致力于面向IT互联网行业,培养软件开发工程师、测试工程师、系统管理员、智能硬件工程师、UI设计师、网络营销、会计等职场人才 达内使命:缔造年轻人的中国梦、缔造达内员工的中国梦 达内愿景:做管理一流的教育公司
  • 商务合作

  • Powered by Discuz! X3.4 | Copyright © 2002-2024, 达内教育 Tedu.cn
  • 京ICP备08000853号-56 |网站地图