许多人说过C ++是与C完全不同的语言,但是Bjarne本人也曾说过C ++是从C扩展而来的语言,因此才是C的++
来源。那么,为什么每个人都在说C和C ++是完全不同的语言呢?除了C ++的扩展功能外,C与C ++有何不同?
许多人说过C ++是与C完全不同的语言,但是Bjarne本人也曾说过C ++是从C扩展而来的语言,因此才是C的++
来源。那么,为什么每个人都在说C和C ++是完全不同的语言呢?除了C ++的扩展功能外,C与C ++有何不同?
Answers:
在1980年代,当C ++开发才刚刚开始时,C ++几乎是C的适当超集。这就是一切的开始。
但是,随着时间的流逝,尽管C和C ++之间的兼容性一直被认为是重要的,但C和C ++都在不断发展并彼此脱节。
此外,C和C ++之间的技术差异使得这些语言中的典型习语变得更加复杂,被认为是“良好实践”的语言也有所不同。
这是人们说“没有C / C ++这样的语言”或“ C和C ++是两种不同的语言”之类的驱动因素。尽管可以编写C和C ++编译器都可接受的程序,但是通常认为该代码既不是优质C代码的示例,也不是优质C ++代码的示例。
void *
而C ++却没有。(未选票)
C ++是C的直接后代,几乎保留了所有C作为子集。C ++比C提供更强大的类型检查,并且直接支持比C更广泛的编程样式。C++是“更好的C”,从某种意义上讲,它支持使用C完成的编程样式,具有更好的类型检查和更多的符号支持(不损失)。效率)。从同样的意义上说,ANSI C比K&R C更好。C ++支持数据抽象,面向对象的编程和通用编程。
它支持使C ++与C“完全不同”的面向对象编程和通用编程。您几乎可以编写纯C,然后使用C ++编译器对其进行编译(只要您进行更严格的类型检查)。但是然后您仍然在编写C-您不是在编写C ++。
如果您正在编写C ++,那么您将利用它的面向对象和模板功能,这与在C语言中所看到的完全不同。
简而言之,在C中被认为是惯用的在C ++中绝对不是惯用的。
实际上,由于人们使用C和C ++的方式不同,因此它们是不同的语言。C的目标是极简主义,其中C ++是一种非常复杂的语言,具有许多功能。
还有一些实际的区别:可以从几乎任何语言轻松调用C,并且通常定义平台的ABI,而从其他库中很难使用C ++。大多数语言都具有C的FFI或接口,甚至包括以C ++实现的语言(例如Java)。
除了显而易见的事实,即C ++支持面向对象的编程外,我想您在这里也能找到答案:http : //en.wikipedia.org/wiki/Compatibility_of_C_and_C++
该文章包含一些代码示例,这些示例显示了在C中可以但在C ++中没有问题的东西。例如:
int *j = malloc(sizeof(int) * 5); /* Implicit conversion from void* to int* */
将C程序移植到C ++通常很简单,并且主要包括修复编译错误(添加强制转换,新关键字等)。
尽管C ++在语法上可以是C的超集-即C程序的任何构造都可以由C ++编译器编译。
但是,您几乎永远不会像使用C程序那样编写C ++程序。该列表可以是无止境的,也可以是有人应该做更多研究以将其作为详尽的报告。但是,我只提出一些关键区别的建议。
本篇文章的重点是C ++具有以下功能,即使可以编译等效的C语言,优秀的C ++程序员也必须将其用作编程最佳实践。
如何通过C ++在C ++中完成
类和继承。这是允许系统化的面向对象的最重要的区别,这使得编程表达非常强大。我想-这一点不需要更好的解释。如果您使用C ++-几乎总是这样,那么最好使用类。
私有化-类,甚至结构都有私有成员。这使得类的封装成为可能。C语言中的等效项是通过将对象作为void *类型转换为应用程序,从而使应用程序无法访问内部变量。但是,在C ++中,可以具有公共类和私有类的元素。
通过引用。C ++允许基于引用进行修改,为此需要传递指针。引用传递使代码保持非常干净,从而更安全地防止指针危险。您可以很好地传递C样式指针,并且可以工作-但是,如果您使用的是C ++,则只要
新&删除与malloc和免费。new()和delete()语句不仅分配和取消分配内存,还允许代码作为分解器的一部分执行,以在链中调用。如果您使用的是C ++,则实际上不宜使用malloc和free。
IO类型和操作符重载如果操作得当,操作符重载可使代码可读或更直观。与<<和>> io运算符相同。使用此方法的C方法是使用函数指针-但这很麻烦,并且仅适用于高级程序员。
使用“字符串”。来自C的char *随处可见。因此,C和C ++几乎相同。但是,如果您使用的是C ++,则使用String类总是更好(更安全),这使您免于数组运行的危险,这几乎是所有事情。
我在C ++ 1中仍然不会喜欢的功能。模板-尽管我在许多代码中都不使用繁琐的模板-事实证明它对于库来说非常强大。在C语言中几乎没有它。
我喜欢C的事情以及C ++中的缺失
使用函数指针的多态算法。在C中,当您运行复杂的算法时-有时您可以使用一组函数指针。这以强大的方式实现了真正的多态。当你在C ++中,你CAN使用函数指针-但是,这是不好的。您应该只使用方法-否则会变得凌乱。C ++类中多态的唯一形式是函数和运算符重载,但这是非常有限的。
简单线程。当创建线程时是pthreads-它非常简单且易于管理。当您需要创建应该是类的“私有”线程(以便它们可以访问私有成员)时,它就变成了。有boost类型的框架,但基本的C ++中没有。
地盘
某些语言功能,即使它们是附加功能,也可以改变实际上需要使用该语言的整个方式。作为一个示例,考虑这种情况:
lock_mutex(&mutex);
// call some functions
...
unlock_mutex(&mutex);
如果上述代码涉及调用用C ++实现的函数,那么我们可能会遇到麻烦,因为这些函数调用中的任何一个都可能抛出,并且我们绝不会在那些特殊的路径中解锁互斥量。
析构函数不再具有便利性,可以帮助程序员避免在那一刻忘记释放/释放资源。RAII成为一项实际需求,因为预见可能会在不平凡的示例中抛出的每一行代码在人类上都是不可行的(更不用说这些行现在可能不会抛出,但以后可能会进行更改)。再举一个例子:
void f(const Foo* f1)
{
Foo f2;
memcpy(&f2, f1, sizeof f2);
...
}
这样的代码虽然在C语言中通常是无害的,但就像C ++中的地狱般的混乱一样,因为memcpy
推销这些对象的位和字节并绕过了诸如复制构造函数之类的事情。这样的功能,如memset
,realloc
,memcpy
等,而C开发人员之间的日常工具用于在比特的相当均匀的方式看待事物,并且在内存中的字节,不和谐与C的更复杂和更丰富的类型系统++。C ++鼓励使用更加抽象的用户定义类型视图。
因此,这些类型的事物不再允许C ++,任何希望正确使用它的人都将其视为C的“超集”。这些语言需要非常不同的思维方式,纪律和思维方式,才能最有效地使用C ++。 。
我并不是在各个方面都认为C ++绝对更好的阵营,实际上,出于某种原因,实际上我最喜欢的第三方库都是C库。我不知道为什么会这样,但是C库在本质上往往更趋于极简(也许是因为缺少这种丰富的类型系统使开发人员更加专注于提供所需的最小功能,而无需构建一些大层次的抽象集),尽管我最终常常只是将C ++包装器放在它们周围,以简化和调整其用途以达到我的目的,但是即使这样做,极简主义也比我更合适。我真的很喜欢极简主义,因为它是图书馆的一项吸引人的特性,对于那些愿意花更多时间寻求这种品质的人来说,也许C会鼓励这样做,
我确实更偏爱C ++,但是实际上我经常需要使用C API以获得最大的二进制兼容性(以及FFI),尽管我尽管使用C作为标头也经常使用C ++实现它们。但是有时候,当您真正进入底层时,例如内存分配器或非常低层的数据结构(并且我敢肯定,在那些从事嵌入式编程的人中还有更多的例子),有时对您有所帮助能够假定您正在使用的类型和数据缺少某些功能,例如vtables,costructor和de析构函数,因此我们可以将它们视为位和字节以随机播放,复制,释放,重新分配。对于特别低级的问题,有时使用C提供的更为简单的类型系统可能会有所帮助,
澄清
在此我想对一个有趣的评论进行更深入的介绍(我发现这里的评论对字符数限制非常严格):
memcpy(&f2, f1, sizeof f2);
如果Foo有任何拥有的指针,它在C中也是“地狱般的统治性破坏”,或者甚至更糟,因为您也缺乏处理它的工具。
这是一个公平的观点,但是我所关注的一切主要集中在C ++的类型系统以及RAII方面。X射线字节复制memcpy
或qsort
函数类型对C造成较少实际危险的原因之一是C f1
和f2
以上的破坏是明确的(如果它们甚至需要非平凡的破坏),而当析构函数移入图片时,它们变得隐含且自动化(通常对开发人员而言具有巨大的价值)。更不用说像vptrs之类的隐藏状态了,这些功能将立即结束。如果f1
拥有指针和f2
浅表会在某些临时上下文中复制它们,如果我们不尝试第二次显式释放那些拥有的指针,则不会造成任何问题。使用C ++,编译器会自动执行此操作。
如果使用C语言编写“ 如果 Foo拥有自己的指针”,这将变得更大,因为资源管理所需的显式性通常会使某些事情通常更容易被忽略,而在C ++中,我们可以不再简单地编写UDT。通过仅使它存储不是平凡的可构造/可分解的任何成员变量即可构造/可构造的(再次以通常非常有用的方式,但是如果我们很想使用诸如memcpy
或的函数,则不是这样realloc
)。
我的主要观点不是试图争辩这种明确性的任何好处(我想说,如果有的话,它们几乎总是被随之而来的人为错误的可能性增加的弊端所压倒),而只是说像memcpy
和memmove
和qsort
和memset
和realloc
在具有像C ++一样丰富的功能的UDT的语言中,等等都没有地位。尽管它们不管存在,但我认为不必说绝大多数C ++开发人员会明智地避免诸如瘟疫之类的功能,而这在C语言中非常日常, d认为,由于C类型系统更为基本甚至“笨拙”,其在C语言中所造成的问题更少。X射线检查C类型并将其视为位和字节容易出错。在C ++中这样做完全是错误的,因为这样的功能正在与语言的非常基本的特征以及类型系统所鼓励的东西作斗争。
然而,这实际上是C语言对我最大的吸引力,特别是在C语言与语言互操作性之间的关系方面。要使诸如C#的FFI之类的东西了解C ++的成熟类型系统和语言功能,到构造函数,析构函数,异常,虚函数,函数/方法重载,运算符重载,所有各种类型的东西,要困难得多。就C而言,这是一种相对愚蠢的语言,就API而言,它已经成为相当标准的语言,许多不同的语言可以直接通过FFI导入,也可以通过某些C API导出函数以所需的形式间接导入(例如:Java Native Interface)。 )。这就是我几乎只能选择使用C的地方,因为在我们的案例中,语言互操作性是一个实际的要求(尽管我经常
但是你知道,我是一个实用主义者(或者至少我努力成为)。如果C是最肮脏和令人讨厌的,易出错的,讨厌的语言,我的一些C ++发烧友都声称它是C(我算自己是C ++发烧友,只是因为某种程度上它不会导致对C的仇恨;相反,这对我产生了相反的影响,使我在各自的方面和差异方面更好地体会两种语言),那么我希望它以一些最笨拙,最漏洞百出的形式出现在现实世界中,用C编写的不可靠的产品和库。我找不到。我喜欢Linux,喜欢Apache,Lua,zlib,我发现OpenGL可以承受长期的传统,例如不断变化的硬件要求(Gimp,libpng,Cairo等)。至少就该语言所构成的障碍而言,似乎似乎并没有造成僵局,只要能胜任地编写一些出色的库和产品,那便是我真正感兴趣的全部。因此,我从来没有对最热情的人如此感兴趣语言战争,除了发出务实的呼吁,然后说:“嘿,那里有很多很棒的东西!让我们学习他们是如何做到的,也许还有一些很酷的课程,不是专门针对语言的惯用本质,我们可以带回来无论我们使用哪种语言。” :-D
memcpy(&f2, f1, sizeof f2);
如果Foo
有任何拥有的指针,它在C中也是“地狱般的统治性破坏”,甚至更糟,因为您也缺乏处理该指针的工具。因此,编写C的人们不会做那么多的事情
Foo
实例现在都可能想要破坏动态数组或向量,或达到此目的的某种东西。在某些特殊情况下,我经常发现能够编写某些数据结构以基于破坏是显式的而不是隐式的事实是有帮助的。
Foo
确实有任何拥有的指针,我们可以为它提供适当的复制/移动ctor,dtor,使用值语义等,并具有更丰富,更安全的代码。但是有时候我发现自己想要在通用的位和字节级别上推广数据结构或分配器的情况下达到C语言,并且我可以对某些类型的假设做出一些假设,这些类型在其狭窄的用例中倾向于简化受了一点这样的假设,我感到更加自信一点在C.使
API void filter_image(byte* pixels, int w, int h);
作为一个简单示例而不是like API void filter_image(ImageInterface& img);
(它将我们的代码耦合到这样的图像接口,缩小其适用性)。在这种情况下,我有时只在C中实现此类功能,因为在C ++中几乎没有收获,并且它减少了此类代码将来需要更改的可能性。