我用C++的理由——关于C和C++的选择

?

摘自:http://www.xue163.com/32/6/325715.html,作者:王可。整理的很好!

首先,我不会使用Java或C#,能力上不会,主观上也不会,因为两点原因:1,他们都属于解释型的语言,这有很多问题是我无法容忍的,程序的速度和封装的安全性;2,他们都不够底层,没有指针,却加载了内存管理器,对我来说这些都是麻烦和束缚,对我而言他们都不是足够自由的语言。或者说,我无法接受他们对使用者的理念,似乎他们认为使用他们的程序员都是懒惰和容易犯错误的,而他们的高级之处都是依赖各种类库,但对底层的操作却有很多限制,如类型转换,非常罗嗦。C和C++就不同了,他们能够给我充分的自由,由程序员来承担自己逻辑错误的后果,和开发需要的类库,这更符合我的个性。?

我现在对C和C++都能熟练地使用,我的工作主要维护着以往的C代码,对这部分代码的修改与补充,我仍然使用C语言;对新的开发任务我都使用C++,如数字图象处理的类库,数据迁移的模块等等。我首先是一名熟练的C程序员,而后逐步掌握C++,最初使用C++是认为他可能比C更方便和高级,不排除有一种追求时髦的情节。现在我同时是这两种语言的熟练掌握者,也曾经犹豫过到底是最终使用C还是C++,主要是担心C++的速度不如C。?

最近看到了关于C和C++的争论,坚持C语言的论调对我有一定的触动,因为我现在基本都是使用C++开发,他们提出的一些C++的弊端正是C++为之骄傲的东西,我从来都没有考虑过这些,这些观点的确非常新颖,且非常有见地;比如,所谓的面向对象和设计模式,我们是否真的需要或只是为追求一些时髦的概念,这引发了我很深的思考。为此我又特意去尝试强制使用C来写已经用C++写好的类库,发现不行,那太罗嗦;我又尝试仅使用C++基本的内容,而不使用高级特性,如继承、虚函数、模板等等,以及直接实现功能类,而不使用复杂的各种类关系,比如我的模板动态数组,需要使用到内存分配器类,对象构造器类,对象分配器类和模板算法集合,发现也不行,我没有办法改变这种设计。?所以,经过这次再尝试,我决定还是选择C++作为我的主要语言工具,我知道C和C++都不能相互替代,但对于我个人,我还是选择C++。我使用C++,完全可以仅使用它和C语言的交集,我可以选择不使用任何高级特性,或选择我需要的那部分高级特性,在适当的位置;但选择C就意味着几乎不可能,虽然C++的一些特性可以在C里将就或有等价实现方法,但那会另你的代码看上去比较累。?

名字空间,我喜欢用名字空间,虽然这不是大问题,但我觉得使用名字空间更舒服。如果是C语言,我就会选择加一串前缀来防止重名或表示这个函数是此函数库的一部分,如:?
lib_func();?

而C++则是:?
lib::func();?

或:?
using namespace lib;?
func();?
我更乐于接受C++的形式,尤其是当你的函数名也很长的时候,C会令它更长。?

在结构体中使用方法,我可以将从属于结构体的函数作为它的成员方法和它声明在一起,这起到了给函数归类的作用,如果是C,或许作者自己是清楚的,但作为调用者就要在一大堆函数中找出操作这个结构体的函数。而且,按照我的习惯,会使函数名更复杂:?

struct StructA a;

lib_StructA_func(&a);?
而C++的形式是:?
lib::StructA a;?
a.func();?

构造函数,我喜欢用构造函数来初始化结构体,这样会避免使用者忘记使用memset,或者有的时候结构体的初始化值并不是全为0,如句柄应该是-1,或者C语言需要单独为初试化写个函数,如:?
struct StructA a;?
memset(&a, 0, sizeof(a));?

或者:?
struct StructA a;?
lib_StructA_init(&a);?

而C++只有一句:?
lib::StructA a;?

拷贝构造函数、赋值运算符、析构函数,同样的道理,重载这些会另使用者考虑更少的问题,代码也更简洁。?

类的作用域控制,它可以很简单地控制哪些成员和方法是供使用者访问的,哪些是内部的成员和方法,而C你想确保函数肯定不会被访问到,就只能写在源文件里,但这样又不能在自己的库内共享,所以只有利用函数名来告戒使用者,这是内部的函数,最好不要直接调用它:?
void _lib_func();?

通常使用下划线起头来代表此类函数,但你仍不能完全消除这种隐患。?

虚函数,我认为它的确有可能非常影响性能,我也是很少使用它,但它作为回调的实现方式的确很好,或者是我个人更乐于接受这种形式吧。在父类中实现算法,然后提供一组虚函数,只要子类继承它并实现那些需要父类在算法过程中来回调的方法,就可以实现回调的目的,这是在C++中一种非常典型的虚函数用法。而作为C,就必须传递函数指针,和书写设置这些函数指针的函数。虽然从本质道理上说,他们是一样的,但C++的形式更让人乐于接受。?

对于虚函数在教课书中描述的用法,以虚类指针去操作子类,可以简化代码的说法,我倒不喜欢用,而且这样的情况其实比较少,而且极度影响性能。?

利用模板类来实现数据结构,是一种对使用者来说非常直观的事情,而传统的C方法,即定义一个void *指针,实现和使用起来是很罗嗦的,如:?
struct Array?
{?
void *buffer; /* 数据地址 */?
int bufsize; /* 内存的总字节数 */?
int typesize; /* 元素的字节长度 */?
int count; /* 元素的有效个数 */?
};?
这种形式有两个问题:1,访问元素需要做指针类型转换,并且没有了类型检查,容易出错;2,如果将数据结构的方法都封装起来,那么肯定没有C++的模板类库快,因为后者大部分函数都是内联的。?
那么就只有第二种形式,即完全用宏来做,这就是所谓的用宏来实现与C++模板类等价的功能,相信很多人都见过那种代码,它几乎可以达到和C++模板类一样的形式:(注意,这些都是宏)?
lib_DeclareArray(a, int); /* 声明一个int数组 */?
lib_Array_Element(a, 0) = 100; /* 访问第一个元素 */?
C++模板类的形式:(在编译后是实例化的真正函数)?
lib::Array<int> a;?
a.Element(0) = 100;?
它甚至可能比C++更快,因为它全是宏,但是它的实现代码可读性太差,而且非常难以调试。?
C++的这一优点在于使用模板的直观和有内联函数的支持,如果没有内联函数,我想我不会选择使用模板类来实现数据结构。?
?
如果说名字空间,成员函数,自动初始化和摧毁,虚函数的回调,这些其实都不是大问题的话,因为只是C++的形式更好一些,那么对我来说,模板和模板类就是不可或缺的。如果要用C实现和C++一样的模板机制,那对程序员的技巧要求太高了,而且产生的代码不仅多而且乱,对于我习惯了用C++的模板,就不会再乐意去使用C的宏技巧。而且C只能使用宏,然后这些宏所代表的代码大段大段地嵌入调用者的代码里,使它的函数变得十分庞大,这就好比一个模板类的所有成员函数都必须是内联函数,而C++是可以选择的。?
模板还有一个好处,就是只需要为算法写一份代码,尤其当算法不太关心或完全独立于参数的类型时,你不使用这项技术,就要写很多份代码。如果类型是有限的,那么还好,你最多就写那么几份雷同的函数,你还可以利用宏的技巧,但如果你的函数需要接受不确定的参数类型,或对无限种可能的类型都有效,那么就不是写多少份代码的问题,而是你根本就不能完美或完全地实现。?
比如copy一个变量到另一个变量,就这么简单的算法,用C++只要实现copy构造函数或赋值运算符,就可以做到:?
template<class t>?
void copy(t &a, const t &b)?
{?
a.~t();?
new (&a) t(b);?
}
如果有利用赋值运算符就更简单:?
template<class t>?
void copy(t &a, const t &b)?
{?
a = b;?
}?
但如果是C,你就必须指定一个赋值函数的地址:?
void copy(void *pa, const void *pb, void (* func)(void *, const void *))?
{?
(*func)(pa, pb);?
}
那这个函数就没有意义了,在C++中写copy构造函数和赋值运算符是一种默认习惯,但在C里写一个独立的复制函数,就不是习惯了。如果没有模板,大量的此类操作都要通过函数指针传递。?
?
异常,如果充分体会返回错误代码和使用异常的区别,那么你就会认为这不是谁替代谁的问题,至少你在C++里就多了一种方式,而在C里你只能:?
int ret = 0;?
if(SUCCESS != (ret = func1()))?
return ret;?
else if(SUCCESS != (ret = func2()))?
return ret;?
else if(SUCCESS != (ret = func3()))?
return ret;?
else?
return SUCCESS;?
看上去很累,而且程序的逻辑看上去很不清晰。?
而C++的形式:?
try?
{?
func1();?
func2();?
func3();?
}?
catch(Exception &e)?
{?
return ERROR;?
}?
你可以选择catch或不管,让更上层的函数去捕获,也可以处理一部分后继续向上抛,或完全将异常处理掉。而你仍然可以选择返回错误代码的方式。?
对我来说,这是两种错误处理的方式,我会根据需要而选择不同的方式,并不是用异常代替所有的返回错误代码。而C语言中也有类似的东西,就是longjmp,所谓的超级goto,它和异常很像,但如果要像C++那样处理多级异常就会很累,因为longjmp必须在错误发生时指定跳转到哪里去的位置,那么就不能像C++那样在任何层次上都可以捕获。不过它也有一个功能是C++的异常做不到的,就是可以跳到更远的地方去,可以转到之前的调用顺序上,而不仅仅是层次,如:?
func1();?
func2(); /* 可以再跳回func1 */?
而C++中func1不可能捕获func2的异常。因此,C++的异常传递顺序,是根据函数堆栈的深度,而C的longjmp是根据过程,它可以回到之前走过的任何位置。但我想自己永远也不可能使用到这个功能。?
?
更重要的,我写的是库,是要给人用的,如果一个变长字符串都是一大堆宏,那还怎么叫人用。开发库就要考虑到使用者的体验和为使用者节约他的代码,而不是为他的程序降低可读性,让他满地都是你的宏名,而他自己的逻辑都被你的代码给淹没了。我认为在这个方面C++做的很好,他是以现代人的理解去展现你的接口的,在C++中简洁的东西性能都是快的,而C做不到C++的简洁,如果要做到最大的性能,却又一定是复杂的。

?

时间: 2024-10-10 02:49:15

我用C++的理由——关于C和C++的选择的相关文章

单元测试工具 - karma

在离开上一家公司之前,team leader 在我离开前留给了我最后几个关键字:karma,断言库,JASMINE,QUNIT,MOCHA. 可一直拖拖沓沓的,没有去了解.直到今天,才终于抽出心情和时间来研究它. 在文章开始之前,首先对前 team leader — 满爷 表示感激. 虽然你不是我所见过的最优秀的前端,但你是我所见的最乐意与小伙伴share经验心得的 team leader. OK,言归正传,开始主题... 关于karma Karma是一个基于Node.js的JavaScript

Netty权威指南

Netty权威指南(异步非阻塞通信领域的经典之作,国内首本深入剖析Netty的著作,全面系统讲解原理.实战和源码,带你完美进阶Netty工程师.) 李林锋 著   ISBN 978-7-121-23343-2 2014年6月出版 定价:79.00元 524页 16开 编辑推荐 - 资深一线专家诚意之作,总结多年实践经验,带你全面掌握Java高并发异步通信的首选框架——Netty. - Facebook.阿里巴巴.1号店.并发编程网.JBoss等多位资深技术专家联名力荐. <Netty权威指南>

软件工程术语(下)

A B C D E F G H I J K L [M] N O P Q R S T U V W X Y Z  major defect主要缺陷      一个工作产品中所存在的那些严重影响产品功能的正确表现.且若在产品开发周期的后期发现将可能比在产品开发前期发现更加难以改正的故障. management管理      软件工程过程中的核心支持工作流程,其目的在于计划和管理开发项目. marshal反串行化      反串行化 (deserialize) 的同义词. measurement dys

性能小贴士

性能小贴士 本文主要介绍一些代码优化方面的小贴士,结合起来使用能整体性的提升应用性能.但是,这些技巧不可能带来戏剧性的性能改变.合适的算法和数据结构是解决性能的首选考虑(还有程序的执行流程优化),但这已经脱离了本文的范畴. 本文介绍的小贴士是每个有追求的程序员应有的编码习惯. 关于如何写出高效的代码,这里有两个基本的原则: Don't do work that you don't need to do Don't allocate memory if you can avoid it 面临的现状

我必须得告诉大家的MySQL优化原理

---恢复内容开始--- 说起MySQL的查询优化,相信大家收藏了一堆奇淫技巧:不能使用SELECT *.不使用NULL字段.合理创建索引.为字段选择合适的数据类型..... 你是否真的理解这些优化技巧?是否理解其背后的工作原理?在实际场景下性能真有提升吗?我想未必.因而理解这些优化建议背后的原理就尤为重要,希望本文能让你重新审视这些优化建议,并在实际业务场景下合理的运用. MySQL逻辑架构 如果能在头脑中构建一幅MySQL各组件之间如何协同工作的架构图,有助于深入理解MySQL服务器.下图展

跟我一起学习VIM - vim插件合集

2016-06-14 15:04 13333人阅读 评论(0) 收藏 举报 分类: Linux(104)  目录(?)[+]  前两天同事让我在小组内部分享一下VIM,于是我花了一点时间写了个简短的教程.虽然准备有限,但分享过程中大家大多带着一种惊叹的表情,原来编辑器可以这样强大,这算是对我多年来使用VIM的最大鼓舞吧.所以分享结束之后,将这篇简短教程整理一下作为我2014年的第一篇Blog. 目录写在前面:Life Changing Editor什么是VIM为什么选VIM为什么选其它为什么犹豫

画饼还是远景?

画个饼给大家瞧瞧,看图吧. 回溯到2013年初,那时有个关于2012年诺贝尔经济奖的新闻,是关于市场交换的规律的 研究,被称为 稳定匹配理论.多数人都让这新闻 过眼烟云了,有个人却注意到了 市场交换和人类婚恋交友行为的 内在相似性,并悄悄的进行了深入的研究.这就是图中那个大点的项目的来历. 这两个创业项目,其中小项目的优点是短平快和刚需.大的这个,是关于婚恋交友的,相关的市场指标: DATE (Yahoo Finance),年收 入~ 6亿¥; Match.com ,十倍于DATE. 有人说,交

跟我一起学习VIM - The Life Changing Editor

前两天同事让我在小组内部分享一下VIM,于是我花了一点时间写了个简短的教程.虽然准备有限,但分享过程中大家大多带着一种惊叹的表情,原来编辑器可以这样强大,这算是对我多年来使用VIM的最大鼓舞吧.所以分享结束之后,将这篇简短教程整理一下作为我2014年的第一篇Blog. 目录 写在前面:Life Changing Editor 什么是VIM 为什么选VIM 为什么选其它 为什么犹豫选择它们 VIM >= SUM(现代编辑器) 如何学习VIM 一秒钟变记事本 VIM的基本用法 VIM进阶:插件 插件

tiny4412sdk-1506原生uboot卡死

于16年2月多购买了tiny4412sdk-1506,用友善之臂(以下简称友善)的superboot是可以进入linux,而用三星原始的uboot_tiny4412-20130729则不可以.出现现象是卡在“OK”, 偶尔能继续跑下去.根据以往开发的经验,可以知道这绝对是ddr3配置的问题,查看“Tiny4412-1306-Schematic”是4颗[email protected],用到一个控制器两个片选(chip0,chip1),而“Tiny 4412-1412-Schematic”(对应我