当我从 C 和 C# 过渡到 C++ 时,我可以期待有啥不同?

Posted

技术标签:

【中文标题】当我从 C 和 C# 过渡到 C++ 时,我可以期待有啥不同?【英文标题】:What can I expect to be difference when I transition from C & C# to C++?当我从 C 和 C# 过渡到 C++ 时,我可以期待有什么不同? 【发布时间】:2008-12-24 12:26:27 【问题描述】:

这是一个简单的问题。

我已经使用 C 和 C# (2.0) 完成了大量工作,但从未使用 C++。在学习 C++ 时,我可以期待有什么不同?会有什么大问题或障碍我也应该注意吗?有没有人推荐给有经验的程序员学习 C++ 的速成课程书籍/网站?

【问题讨论】:

【参考方案1】:

我能想到的主要区别在于,C++ 比 C 和 C# 更像是一种多范式语言。在 C# 中,OOP 仍然是 范式。它首先是一种 OOP 语言,如果您不使用 OOP,C# 社区会告诉您您做错了。 (尽管在过去几年中,C# 也为一些函数式编程添加了很好的支持)。

在 C++ 中,OOP 是受支持的,您可以在喜欢的时候使用它,但所有大惊小怪都围绕着泛型编程。 C++ 模板允许广泛的智能、可重用和通用库,并实现许多与老式 OOP 相同的目标,但没有大的继承层次结构,并且几乎没有类之间的耦合。标准库包含很多这样的例子

在 C++ 中,许多 C 构造虽然仍然合法,但基本上都被避开了:

原始指针(通常替换为智能指针,例如 boost::shared_ptrstd::auto_ptr,或替换为引用 用户代码中的内存分配(通常应包装在智能指针中,或自定义的 RAII 对象中) 函数指针(通常用函子代替,以提高类型安全性和性能) goto(在 C 中经常用于跳转到清理代码。同样,RAII 不再需要) 预处理器(实际上从不需要。更喜欢模板)

当然,上述每一点都有例外,但根据一般经验法则,C++ 代码与 C 代码不同,几乎不再使用这些。

与 C# 相比,类确实是承担大量繁重工作的主力。在 C# 中,一个类只不过是一个脚手架,一个将所有方法放入其中的容器。当然,它有一个构造函数,并且它可以实现 Dispose();但 C++ 更进一步,你有:

构造函数(就像在 C# 中一样,从头开始初始化类) 复制构造函数(将一个类初始化为另一个对象的副本) 赋值运算符(因为类是 C# 认为的值类型。因此赋值不仅仅是更改引用,而是以用户定义的方式复制对象的所有内容) 析构函数

析构函数可能是 C++ 中最重要的概念。 RAII 是管理内存或其他资源的方式至关重要,因为当对象超出范围时会自动调用它。这使您的类可以做出很多在 C 或 C# 中无法实现的保证。 例如,boost::thread 提供了一个作用域锁,当它超出作用域时,无论函数正常返回、抛出异常还是其他任何事情,都保证释放它。所以在使用这个库时,用户不必担心释放锁或其他资源。一旦你完成它们,它就会自动发生。

从某种意义上说,这为您提供了更多自定义类行为的钩子。与 C# 不同,您可以准确控制执行简单赋值时发生的情况。您可以控制当类超出范围、从头开始初始化或作为另一个对象的副本时发生的情况。这使得编写良好的类几乎不可能被错误地使用。 (几乎)

除此之外,模板和模板元编程是您可能会遇到的概念。它们是非常强大的工具,因此请确保您与它们保持友好关系。 :)

【讨论】:

【参考方案2】:

以下是我的一些观点:

指针在 C++ 中扮演着重要角色。 在 C# 中,您只有参考 类型。你可能会头痛 指针错误。 您还必须管理 手动分配内存。和 你的程序有可能 如果这样,将经历内存泄漏 没有正确完成。在 C# 中,每个 对象被垃圾回收, 自动。 的声明和定义 类通常被分开 头文件 (.h)源文件 (.cpp 或 .cc)。这将导致 一开始有点困惑。 你不会有这么多的基类 C++ 中的库支持与 C# 中一样。为了 例如,你不会有 HttpRequest C++ 中内置的类。 因此,您必须使用 很多外部图书馆并且知道 如何处理 .dll 或 .lib。 C++ 没有Interface,就像在 C# 中一样。你 将使用抽象类(和 多重继承)。 C++ 中的字符串是 char 数组或 指向字符的指针。 有一些算法和数据 结构可用于 标准模板库 (STL.) 但是 在你之前学习需要时间 可以开始有效地使用它:)

您还可以学习 C++/CLI,将您的 .NET 代码和本机 C++ 混合在一起,以获得两全其美的效果。

【讨论】:

如果他以前用过 C 语言,那么指针和内存管理应该不会有什么惊喜。 我非常不同意第 1 点和第 2 点。在现代 C++ 中,您从不使用 delete,很少使用 new。内存管理是通过 RAII 进行的。实际上,我在 C# 中遇到的内存泄漏比在 C++ 中 更多 @Alexandre 诚然,当时我并不知道现代 C++ 的一些更好的实践。现在,当我在 C# 和 C++ 应用程序中看到越来越多的 memleak 时,我知道它们同样难以追踪 :(【参考方案3】:

我强烈建议:“有效的 C++ 和更有效的 C++”。如果你愿意,“Effective STL”可能会有所帮助:)

这些书是专门为需要有效处理 C++ 的聪明程序员编写的。我证明,当我开始使用 C++ 时,Effective C++ 对我的帮助很大!

另一本适合 C++ 初学者(但我不认为是计算机初学者)的好书是《Accelerated C++》。它主要关注良好的编码、stl 和生成高级代码,而不是关注细节。 (这些在本书的结尾有更多的介绍)。

祝你好运:)

【讨论】:

【参考方案4】:

嗯,我已经使用 C++ 10 多年了,所以我的答案是,学习严格的内存管理、C++ 模板、STL,然后是 Boost。这应该需要您几个月的时间,然后细节将需要您说 5 年 :)

至于书籍,我喜欢 Herb Sutter 的 Exceptional C++ and More Exceptional C++

【讨论】:

嗯……你每天都会学到新东西。与普通 C 相比,C++ 中哪些内存管理方面是新的?我一直认为这些讨厌的东西已经在 C 中了。 首先你使用 new/delete,而不是 malloc/free,接下来你有 new/delete 重载,然后你有实例分配器,类分配器,智能指针,举几个明显的例子一个 谢谢!现在,当我回到 C++ 时,我知道了更多需要注意的事情。 你是对的,讨厌的东西已经在 C 中了。告诉 C 程序员“在 C++ 中你必须学习内存管理”是非常误导的。 C++ 只是添加了各种包装内存分配的方法。【参考方案5】:

嗯……很难。如果你对 .NET 和指针没问题,就不应该有太多新的东西。

我认为处理 C++ 头文件将是一种体验。您还可以熟悉 C++ STL。

您可能还必须应对有人对您大喊大叫,比如多重继承。

【讨论】:

【参考方案6】:

我从 Asm 转到了 C、C++……,最近又转到了 C#。它减轻了能够实例化对象并从方法或属性中返回它们的能力。通常,实现和用户代码都不需要担心释放内存。此外,在大多数情况下,没有理由返回 HRESULT 之类的状态代码,因为如果出现问题,您会抛出异常并让用户代码根据需要处理它。

最近为我的上一个项目切换回本机 C++ 代码,我真的很怀念垃圾收集和异常抛出。

不过,我确实喜欢 C++ 的模板功能。 C# 应该有一天会扩展这种技术。

【讨论】:

为什么不用C++异常? Native C++ 的异常处理不如 CLR 强大。我没有发现它有太多用处,因为我习惯于以 CLR 方式抛出异常。【参考方案7】:

我看到一些人指出内存管理是一个大问题,因为您必须手动完成。好吧,不要相信他们,这是错误的。除非您处于异国情调/老式环境中,否则 C++ 具有帮助我们隐式和确定性地管理内存(以及任何其他资源)的工具。请参阅 boost/std::tr1 shared_ptr 和 RAII。

与 GC 收集内存的最大区别是:您必须自己处理循环。

关于多重继承,一旦你掌握了 LSP 的含义,这也不是什么大问题。

我必须同意pheze 的信息:Accelerated C++ 如果您想教您如何(/应该?)使用 C++,则必须阅读。

【讨论】:

【参考方案8】:

Jalf 说的完全正确 - 来自 C/C# 背景,您几乎已经了解所有需要的基本 C++,并且您也了解 OOP,所以您已经做得很好了。

不过,有 2 件事会浮现在您的脑海中,这对您来说是新的:

RAII:这让您拥有自动内存管理,您不再需要记住释放所有您 malloc 的东西。酷啊!它还对其他所有资源都有帮助,您不再需要记住关闭该套接字、文件、数据库连接 - RAII 会为您完成。 C 和 C# 都没有这个。

STL/Boost:这是“C++ 类库”,充满了有用的实用程序和位。所有标准容器都在 STL 中(如“字典”、“数组”和“列表”)以及您可以应用于它们的算法(例如,如果您有数据项列表,您可以提供一个小函数对象(称为一个仿函数),然后将两者都传递给一个通用算法。这真的很强大,你可以用它做很多工作)。

【讨论】:

【参考方案9】:

主要区别在于 C++ 是基于值的,而 C# 是基于引用的。

在 C# 中,将对象传递给方法时,实际上只是传递了一个引用,因此调用方法和被调用方法都在查看同一个对象。

在 C++ 中,当您将对象传递给方法时,会生成该对象的副本,并将整个副本传递给方法。被调用方法对副本所做的更改不会影响调用方法中的原始内容。可以使用参考语法在 C++ 中模拟参考行为:

void somemethod(someclass& obj);

【讨论】:

我想说,引用行为可以通过指针获得,但引用提供了更好的解决方案。我的理由是,虽然它们被称为引用,但在 Java 和 C# 中 references 都是 指针 我不知道你为什么将 & 引用称为模拟,因为它们是 C++ 的关键和原始特性,而不是一些晦涩的扩展。 这是一种“模拟”,因为参考语法并未使 C++ 成为基于参考的语言,但确实提供了许多相同的功能。【参考方案10】:

最大的“障碍”可能是你要负责释放你分配的内存

【讨论】:

他来自 C 背景,C++ 中的内存管理较少,而不是更多。【参考方案11】:

正如已经发布的那样,您可能会负责您的内存管理,但除了学习 C++ 库以及如何组织包括它们之外,我认为这根本不应该是一个艰难的转变。

【讨论】:

【参考方案12】:

关于语言实现的差异——我认为你需要小心内存管理、头文件中的声明等——我认为最难处理的是特殊的额外重载语法中的字符。经过多年编写 C# 代码后,我发现最多的就是那些多余的 *&<>。我的第一反应是 C++ 代码看起来很像一个大的正则表达式。

我敢肯定,一旦你使用了一段时间,这种情况就会消失,但我不再做足够的 C++ 来确保我确切地知道在每种情况下使用哪个符号——我是否传入了一个指向该对象的指针或引用?

在阅读其他人的代码时,另一个大问题是运算符重载。我知道你可以在 C# 中做到这一点,但我很少看到它在那里完成。可能事情已经改变了,但我曾经在 C++ 中看到很多运算符重载,+/- 会产生一些奇怪的效果。除了一些相当明显的胜利(比如字符串连接)之外,我认为这确实是一个我可以不用的可疑特性——在 C++ 和 C# 中。

【讨论】:

【参考方案13】:
    内存管理 更复杂的语法 内存管理 在整个 OOP 生命周期中,工具支持少于 C#。 内存管理

我提到内存管理了吗?

我一直认为,将市场用作衡量部分人群感知需求的指标很有趣。考虑到(为了对比)VB 市场,组件库似乎是主要重点。然而,从早期开始,C++(和 C)市场中的主要产品似乎一直是内存管理、泄漏检测和代码质量工具(例如 lint-like 检查器)。

【讨论】:

【参考方案14】:

类之间的循环引用[在 C++ 中]。如果你问我,C++ 中的 OOP 真的是半生不熟。

编辑:好的,我想更多的细节是有序的。假设你有: Foo 类和 Blah 类。 Blah 类引用 Foo 类,Foo 类引用 Blah 类。假设你走的是天真的路线,你可能会像这样实现它:

#include <blah.h>
class Foo 
    Blah blah;
    ...

还有废话:

#include <foo.h>
class Blah 
     Foo foo;
     ...

这可能行不通。相反,您需要使用前向引用,即 Foo 变为:

class Blah;

class Foo 
    Blah blah;
    ...

如果您对“C++ 中的循环引用”进行一些谷歌搜索,您就会明白我在这个问题中提到它的原因。现在停止投票给我好吗...

【讨论】:

不确定为什么您似乎认为 C# 不能有循环引用。不用说,你错了 (blogs.msdn.com/nickmalik/archive/2005/03/18/398601.aspx)。 嗯,不,我说的是 C++ 中类之间的循环引用,即需要不只是盲目地包含头文件,认为它们就像普通的导入一样。我将编辑我的问题以反映这一点。

以上是关于当我从 C 和 C# 过渡到 C++ 时,我可以期待有啥不同?的主要内容,如果未能解决你的问题,请参考以下文章

当我从 C# 代码调用导入的 C++ 函数时,为啥会引发 AccessViolationException?

与 C++ 结构等效(或更好)的 C# 结构

从 C# 调用 C++ DLib 导致错误分配异常

当我从jdk8过渡到jdk17有哪些明显的变化

当我从jdk8过渡到jdk17有哪些明显的变化

命名管道:C# 服务器、C++ 客户端