在C++代码中使用/混合C语言?

28

在C++中使用C语言是否不好?

很多人告诉我,在C++中使用C语言是不好的,因为它不太安全,而且需要更多的内存管理。但我一直告诉他们,只要你知道自己在做什么,并且删除你的 "new "和释放你的 "malloc",那么C语言就不会成为问题。

目前我在一个论坛上看到了关于std::stringchar* 之间的辩论。有些人说分配一个简单的 char* 内存块更有效率,只要你在使用完后释放。另一方面,我们也有人认为std::string 更优越,因为它没有涉及到内存管理,尽管不如前者高效。

所以这里的主要问题是:

  • 混合使用C/C++是否不好?编写C++时是否应该只使用100%的C++?

欢迎任何回答!


1
C++是C的超集。因此,任何有效的C程序都是有效的C++程序,而且不存在将C“混合”到C++中的情况。 - Jonathan
35
@Jonathan:int main() { int class = 2; } (这是有效的C代码,但不是有效的C++代码)。C++不是C的超集;C和C++共享一个公共子集。 - James McNellis
9
@Jonathan:噢,拜托,你非常清楚他在说什么。没有人能够说服我printf是一个C++函数。 - Armen Tsirunyan
4
“混合”一词,是否指实现项目的某些部分使用 C 语言,而另一些部分使用 C++ 语言,并在两种语言之间进行清晰的分离?还是像您第三段所暗示的那样,将两种语言的惯用语混合在一起,形成可怕的混合语言? - Mike Seymour
2
@Nick T:如果FQA在解决问题时有用,那么这个问题一定非常奇怪。要不然,你可能不想要一个真实的答案。 - David Thornley
显示剩余8条评论
12个回答

23
我一直告诉他们,只要你知道该做什么,并删除您的 `new` 和释放您的 `malloc`,那么 C 就不是问题。
这是真实的; 如果您非常小心并确保手动清理东西,则它就不是问题。 但是,您真的有时间这样做吗?每次调用 `new` 都可能引发 `std :: bad_alloc`。 您始终会捕获可以抛出和手动清理任何资源的异常吗?
我敢猜测答案是否定的,因为编写这样的代码非常乏味,在罕见的失败情况下即使是正确的也很难确定。
如果答案是“是”,那么为什么要浪费这么多时间来担心资源管理呢?C ++惯用法(如作用域绑定资源管理(SBRM;更常见的是资源获取即初始化(RAII)))和标准模板库等库可帮助您更轻松地编写正确的代码。为什么要走一条艰难的路,当您不必这样做时?
应该在编写 C++ 时仅使用100%的 C++ 吗?
是的,尽管如果有执行所需操作的 C 库,或者如果您有要使用的遗留 C 代码,您可以肯定使用该代码;只是要小心。与 C 代码交互的最清晰方法通常是编写其周围的 C++ 包装器。

2
我没有看到你的回答中有任何关于“在C++项目中使用C”的具体内容。你的论点似乎只是针对C语言,因此属于“选择你的毒药”范畴。 - Javier
1
@Javier:不,我的回答特别针对在C++代码中使用C。C中没有异常,因此您始终知道函数何时可能返回以及何时必须清理资源(除非使用longjmp之类的疯狂用法)。手动管理C中的资源需要一些工作;在C++中几乎不可能。 - James McNellis
2
哦,没错。异常是不从C中调用C++的一个很大的原因。但是从C++中调用C是可以的(而且更为常见)。 - Javier
@JamesMcNellis,前进至2018年 :) 我想在始终更新的VS2017中将一个单独的C文件添加到我的CPP项目中...因此...那个C文件是哪个版本的?我可以看到它不是cpp,但我也可以看到它不是C99..有没有办法可以知道呢? - user10133158

13

我坚信你的问题与C或C++无关。你的问题是关于为了安全而牺牲效率的权衡。是的,C可能更加高效。但是它有多么高效? 以及你为此付出了什么代价? 这些都是你应该回答的问题。 在大多数情况下,string vs. const char* 的开销是不明显的。如果你正在开发一个极度关注效率的应用程序,那么为什么不一开始就用C编码呢?


1
严格来说,使用C++可以获得额外的功能(这些功能不一定会降低性能)- 但有时为了获得原始性能(sprintf vs. iostreams,atoi(& friends) vs. lexical_cast等),您必须牺牲标准。 - Nim
有很多个人喜欢C而不是C++的原因,性能只是其中一个次要的原因。当我使用C++时,我更喜欢完全使用C++;但我真的理解对许多事情的厌恶。顺便说一下,std::string是我最不喜欢的之一,给我一个char*就好了。 - Javier
@Armen,但为什么不把两者的“最佳”结合起来呢?在我看来,持极端观点意味着你在某种程度上局限了自己,这里没有绝对的黑与白,你应该根据需要选择最合适的方法,然后使用它… - Nim
@Nim...除了创建额外字符串对象导致的小性能损失之外,使用+进行多个字符串连接有什么问题吗?在许多情况下,它比使用append更易读,并且比使用字符串流更类型安全(尝试将MFC / ATL CString流式传输到STL流中并查看发生了什么)。 - Gerald
1
@Gerald,在我的领域中,这种“小的性能损失”相对于其他所有正在进行的事情来说是相当高的,所以对我们来说这是一个大问题,但有些问题可以通过std::string的其他功能来缓解,而不必诉诸char*!别误会,就像我说的,我喜欢std::string,我一直在说的是你不应该限制自己只使用其中之一... @Armen,C++Ox解决了很多有用的问题,但在我熟悉的生产环境中,为了利用它们,升级速度非常缓慢。 - Nim
显示剩余12条评论

12

我对回答和相关评论中的极化感到非常惊讶。

在我看来,答案很简单:

编写C++项目时,请使用C++,避免使用C(及其衍生版本),并坚持使用标准库和STL。确保具有同质的C++接口(毕竟这是一个C++项目!) 当在C++项目中使用外部以C编写的项目时,当然可以使用它。(见下面的示例)

两个主要的例子:

  1. 编写进行科学计算的C++程序/库。我肯定会使用GSL(GNU Scientific Library),因为它是用C编写的。只需要注意一些细节(例如专门为特定结构体和GSL内部函数提供的初始化和释放函数),可以用std::unique_ptr typedef进行吸收。还有错误处理问题:如果必要/想要的话,可以将检查错误代码抽象成异常机制,或者可以将错误代码包含在计算函数中。GSL确实有一种设置错误处理程序的方式,我想其他一些C库也有类似的功能。

  2. 使用基于C的Win32 API编写C++程序,这是非常基于C的。我谈论的是轻量级API使用,比如读取目录中的文件,检查文件是否存在等等,并非GDI+或其他复杂的东西。我喜欢用漂亮的C++风格函数包装Win32 API公开的所有C函数,可能还需要必要的异常并返回一个std::string,而不是必须将char*缓冲区作为参数传递。

我知道这两个例子都相当...表面...但我觉得它们表达了一个足够一般的想法。


5

混合使用C和C++是不好的,原因与性能或安全性无关:

它会影响可维护性:

  • C++程序员希望所有代码都像C++一样运行。
  • C程序员希望所有代码都像C一样运行。

因此,当你混合使用C++和C时,你破坏了两个程序员对事物应该如何工作的期望。


4
当然,答案是:这要看情况。
通常来说,你要避免混合使用可能会导致混淆的东西,这可能会导致难以找到的错误。仅仅因为“你知道你在做什么”并不意味着下一个要接触你的代码的人也知道你在做什么。如果你正在开发只有你自己会使用的代码,那么这可能还好,但这种情况很少见。
如果你小心谨慎地使用C语言来提高性能是可以的。但是,只有当你确定需要性能时才应该这样做。过早地进行低级优化是魔鬼的工作。
在非常罕见的情况下,使用char*而不是std::string将带来任何明显的性能优势,并且它只在确实有用时才值得处理内存管理的麻烦。

2
关于 std::stringchar* 的争论: std::string 不会比堆上的 char* 慢;许多实现更快,因为它们使用私有内存池。而且无论如何,std::string 的健壮性远远超过任何(不太可能的)性能损失。

即使实现不比使用C风格的字符串更快,我认为string类的实用性弥补了任何性能损失。除非您未能满足硬性性能要求并且已经执行了所有高级优化,否则不要为了性能而牺牲可维护性和可读性。 - John Bode

2

这里更好的问题是,为什么使用C语言?因为性能?首先,我认为对于具有相同功能的程序来说,没有可衡量的性能差异。其次,你需要分析并证明在你特定的情况下,C++更慢。第三,使用C而不是C++会牺牲大量应用程序安全性。


2
这取决于你如何做。我从未深入研究过源代码,但我发现对于特定用途,Boost.Regex(使用C++开发)在长字符串的正则表达式匹配方面大约比PCRE慢100倍。这可能是设计中的高级性能问题,但它可能是因为它坚持通常“接受”的C++习惯用法的方式。你必须在那里操作原始的C-字符串才能获得最佳性能。 - Gerald
@Gerald:这是完全不同的事情。首先,你实际测量并确定boost::regex比PCRE慢。我假设你接着确定这是你应用程序中的实际性能问题。然后你使用了PCRE。如果boost::regex是你自己的代码,那么只有在你确定boost::regex无法进一步修复或优化时,你才会使用PCRE。 - Puppy
1
没错。我只是对你的说法有点苛求,即“我认为具有相同功能的程序没有可测量的性能差异。”这取决于你有多么坚持“C++-ness”,或者类似的东西 ;) - Gerald

1
简单的答案是,配置文件;确定哪种在你的情况下最好,并明智地使用它!


1

在C++程序中使用C是不好的吗?

虽然这是一个主观问题:在我看来,尽量避免在C++程序中使用C。

很多人告诉我,在C++中使用C不好,因为它不如安全,并且需要更多的内存管理。我一直告诉他们,只要你知道自己在做什么,并删除你的new和释放你的malloc,那么C就不是一个问题。

你正在重新引入c++旨在克服的缺陷和危险,并且这不是c++程序的做法。

我会定期检查/拒绝/重写进入代码库的“具有C ++功能的C”或“具有C功能的C ++”代码。我甚至会将malloc、free等修改为assert在根名称空间中(以及其他事情)。

我目前在一个论坛上,讨论std::string与char *之间的争论。有些人说,分配一个简单的char*内存块更有效率,只要你释放它就可以了。另一方面,我们有人说std::string更优越,因为它没有涉及到内存管理,但效率较低。

在 c++ 中,表示字符串的选项比 std::string 更多。

依我之见,完全可以创建一个新类来表示字符串并服务于特定目的,或者遵循附加合同(必要时)。这种字符串表示的一部分合同是(当然)使用new[]/delete[]来管理它们自己的资源,当使用动态内存时。

如果效率非常重要,而对于特定任务来说 std::string 不够理想,那么 c++ 足以表达您在这些特定情况下的意图,通过创建 c++ 中的专用接口。有很多情况下这是可接受的(依我之见),但不总是值得投入时间。无论如何,与将 C 语言习惯 / 风格 / 危险整合到 c++ 程序中相比,这更容易管理。

因此,主要问题是:混合使用 C/C++ 是不好的吗?在编写 c++ 时,你应该仅使用 100% 的 c++ 吗?

最好为您的需求创建可重用的基于对象的解决方案。提供的示例中的危险可以完全封装起来(如果此优化确实值得投入时间),并编写以使用 c++ 习惯方式的代码,而不会损失性能并具有更好的可维护性。


1
stringconst char * 的特定情况下,对于所有保存 字符串常量(仅限字符串常量)的变量,应使用裸的 const char *,仅在传递给需要 string 的 API 时才转换为 string。始终如一地执行此操作可以消除大量全局构造函数,不会引起内存分配问题(因为字符串常量是永久的、常量数据),并且实际上使代码更清晰 - 当你看到 const char * 时,你知道那将是一个字符串常量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接