欧易

欧易(OKX)

国内用户最喜爱的合约交易所

火币

火币(HTX )

全球知名的比特币交易所

币安

币安(Binance)

全球用户最多的交易所

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

时间:2022-10-09 17:44:28 | 浏览:2101

原文声明:本文为 CSDN 翻译,转载需注明来源。作者 | Martin Sústrik 译者 | 弯月 责编 | 屠敏出品 | CSDN(ID:CSDNnews)以下为翻译正文:首先声明,在整个职业生涯中,我一直在使用C++,而且在做大多

原文

声明:本文为 CSDN 翻译,转载需注明来源。

作者 | Martin Sústrik

译者 | 弯月 责编 | 屠敏

出品 | CSDN(ID:CSDNnews)

以下为翻译正文:

首先声明,在整个职业生涯中,我一直在使用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++,但不使用异常。如今我的这个项目就是这样实现的。

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

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

class foo{public:foo ;int init ;...};

在创建实例时,会调用构造函数(这个函数不会失败),然后调用init函数(这个函数可能会失败)。

与C语言相比,C++代码更复杂:

struct foo{...};int foo_init (struct foo *self);

然而,C++代码真正的问题在于,如果开发人员在构造函数中编写一些代码,会发生什么?

在这种情况下,会出现一个特殊的新对象状态。由于对象已构造,但尚未调用init函数,因此是“半初始化”状态。我们应该修改对象(特别是析构函数)来处理这个新状态。这意味着,给每个方法添加新条件。

有人可能想说,这还不是因为你人为地添加了不使用异常的限制?!如果构造函数中抛出异常,C++运行时会正确地清理对象,不会出现“半初始化”状态。

话虽如此,然而问题在于,如果使用异常,如上所述,就必须处理所有与异常相关的复杂性。对于一个需要在遇到故障时表现出优秀的健壮性的基础设施组件来说,这不是一个合理的选择。

此外,即使初始化没有问题,对象的销毁也绝对会遇到问题。你不能在析构函数中抛出异常。这可不是我强加的人为限制,而是因为如果在进程中调用析构函数,或者恢复栈时恰好抛出异常,就会导致整个进程崩溃。

因此,如果销毁可能失败,你就需要两个单独的函数来处理它:

class foo{public:...int term ;~foo ;};

这就遇到了与初始化相同的问题:一个“半终止”状态,我们必须以某种方式处理,向各个成员函数添加新条件。

class foo{public:foo  : state (semi_initialised){...}int init {if (state != semi_initialised)handle_state_error ;...state = intitialised;}int term {if (state != initialised)handle_state_error ;...state = semi_terminated;}~foo {if (state != semi_terminated)handle_state_error ;...}int bar {if (state != initialised)handle_state_error ;...}};

与之相比,C语言的代码如下。其中只有两种状态。未初始化对象/内存,我们无需担心上述问题,而且结构可以包含任意数据。而且只要对象进入已初始化的状态,就可以正常工作。因此,对象中不需要状态机:

struct foo{...};int foo_init {...}int foo_term {...}int foo_bar {...}

考虑一下,如果在上述代码中添加继承,会发生什么。C++允许将基类初始化为派生类构造函数的一部分。如果抛出异常,就会破坏已成功初始化的对象:

class foo : public bar{public:foo  : bar  {}...};

然而,一旦引入单独的init函数,状态的数量就会开始增长。除了未初始化、半初始化、初始化和半终止状态之外,你还会遇到这些状态的组合。你可以想象一个基类已完全初始化、但派生类半初始化的对象。

对于这样的对象,几乎不可能确保其行为不出问题。对象的半初始化和半终止部分有很多不同的组合,并且鉴于它们只在非常罕见的情况下才会引发故障,因此大多数相关代码可能未经测试就进入了生产。

综上所述,我认为,如果你的需求是不允许出现未定义的行为,则不适合面向对象的编程。这个问题不仅限于C++,任何具有构造函数和析构函数的面向对象语言都不适合。

因此,更适合面向对象语言的项目是:对开发速度有要求、但对“不存在未定义的行为”没有太高要求。

这个问题没有灵丹妙药。系统编程选择C语言更为合适。

相关资讯

C语言入门:深度解析C语言学习体系,一文吃透C语言

01.介绍篇操作系统及开发系统02. 入门篇03.提高篇03.精通篇C语言精通大牛阶段C语言EasyX图形编程如果你与我志同道合于此,很愿意与你交流如果你喜欢我的内容,欢迎关注和支持C语言基础入门资料:素材资源:想要资源的小伙伴可以可以【点

一个资深C语言工程师:浅说C语言的重要性以及如何学好C语言

前言C语言属于高级程序语言的一种,它的前身是“ALGOL”。其创始人是布朗·W·卡尼汉和丹尼斯·M·利奇。C语言问世时是带有很大的局限性,因为它只能用于UNIX系统上。然而随着科学技术的进步,计算机工业的发展,C语言逐渐脱离UNIX。198

知道并没有什么用的C语言背景,了解C语言是一种结构化的编程语言

C语言是一种结构化的编程语言。它也称为面向函数的编程语言。C语言是由美国贝尔实验室(AT&T)的Dennis Ritchie于1972年开发的。1968年,丹尼斯·里奇(Dennis Ritchie)开始研究诸如BCPL,CPL之类的编程语

「C语言开源项目」盘点GitHub上不错的4个C语言项目

大学时接触的第一门语言就是 C语言,虽然距 C语言创立已过了40多年,但其经典性和可移植性任然是当今众多高级语言中不可忽视的,想要学好其他的高级语言,最好是先从掌握 C语言入手。今天盘点 GitHub 上不错的四个 C语言 开源项目,分别是

C语言多关卡推箱子,兄台了解一下?没错,C语言完整简单项目实战

很高兴你能光临小编寒舍首先感谢百忙之中你能从万千文章中点小编得专属页面。这不是娱乐篇,这是学习道场。开始前,小编就做一个简单得自我介绍:(开启装逼模式)我就是传说中写文章通俗而不庸俗,说话风流而不下流,智慧与美貌并重,英雄与侠义得化身得小编

C语言编程:最常见7道C语言面试题,还是有不少人弄不明白?

C语言是一门面向结构化的高级编程语言(也有人认为它是中级语言),用于通用编程需求。基本上,C语言是其基本语法和库函数的集合,因此程序员定义自己的函数并且将其包含在C语言库中也是很方便的。C语言的主要用途是编写其他编程语言的编译器、操作系统、

从零开始学习C语言丨C语言简介和VSCode配置全解

上一篇文章中,简单地介绍了一下什么是编程,以及编程根据编译运行的方式有哪些分类,还附带一些些小小的学习建议。从这篇文章起,就正式进入了C语言学习中。C语言简述首先来认识一下,什么是C语言?C语言是一种计算机程序设计语言,于1972年诞生于美

C语言入门了解篇—C语言发展史

前言作为理工科的我们,看到一些理论、概念和一些术语,就是脑瓜疼。在大学时,这些知识,很多时候都是作为填空题,最终的答案就跟四级作文差不多,答案脑洞大开。回归正题,其实,有很多东西我们只需要了解一下就好了,知道有这么一回事。例如,C语言是谁搞

经验分享丨如何用最短的时间学会C语言,并掌握C语言的精髓所在?

看了此文后,我想对于您学习此语言一定有很大的帮助,同时也能了知晓如何用最短的时间学会C语言以及掌握C语言的精髓所在。谈及C语言,我想凡是学过它的朋友都有这样一种感觉,那就是“让我欢喜让我忧。”欢喜的是,C语言功能非常强大、应用广泛,一旦掌握

如何学习C语言?很容易,C语言从入门到入土,从长发飘飘到秃顶

学C语言不知道从哪儿下手?学习框架都帮你列好了!在未来计算机专业讲又要迎来一批新生小鲜肉!C语言入门到入土,从长发飘飘到秃顶的老铁也越来越多,其中,想自学成才的也占了不少数;想笨鸟先飞的也占有不少数。传说中的C语言就是那么刺激和高挑战没有基

C语言入门了解篇—C语言编译流程

C语言入门了解篇-C语言编译流程(面试常见问题)当我们安装了集成环境之后,编写.c文件,一按下图红色框框的按键即“编译并运行”,就可以得到一个“.exe”后缀的可执行的C程序文件,这期间并非只有一道工序,而是分别经过了预处理、编译、汇编和链

C/C++编程笔记:C语言编程知识要点总结!大一C语言知识点(全)

一、C语言程序的构成与C++、Java相比,C语言其实很简单,但却非常重要。因为它是C++、Java的基础。不把C语言基础打扎实,很难成为程序员高手。1、C语言的结构先通过一个简单的例子,把C语言的基础打牢。C语言的结构要掌握以下几点:(1

C语言从入门到精通:C语言的发展进程

C语言是一种计算机程序设计语言,它既有高级语言的特点,又具有低级汇编语言的特点。它可以作为系统设计语言来编写工作系统应用程序,也可以作为应用程序设计语言来编写不依赖计算机硬件的应用程序。因此,它的应用范围非常广泛。C语言的诞生及发展历程如图

C语言基础之C语言概述

C教程更多C/C++学习资料 私密我“代码”,即可获取C是贝尔电话实验室的Dennis M. Ritchie于1972年开发的一种通用的,程序性的,命令式的计算机编程语言,用于开发UNIX操作系统。C是使用最广泛的计算机语言。它与Java编

什么是C语言?C语言的简介

C语言是一种计算机程序设计语言,它既具有高级语言的特点,又具有汇编语言的特点它可以作为工作系统设计语言,编写系统应用程序,也可以作为应用程序设计语言,编写不依赖计算机硬件的应用程序因此,它的应用范围广泛,不仅仅是在软件开发上,而且各类科研都

友情链接

网址导航 SEO域名抢注宝宝起名网妈妈知道币圈今日聊城荆州新闻头条网玉石网今日太原今日淮北捷豹跑车网爱马仕奢侈品梵净山旅游攻略手机数码网黄鳝养殖技术嵩山少林寺资讯网流浪地球2影评网福建土楼资讯网三清山旅游攻略美容整形网