0

很久以前我发现了一篇文章(我找不到它 ATM),其中说明了 C++ 中的 new 关键字不好的原因。我不记得所有的原因,但我记得最多的两个是你必须将 new 与 delete 匹配,new[] 与 delete[] 匹配,并且你不能将 #define 与 new 一起使用,就像你可以与 malloc 一样。

我正在设计一种语言,所以我想问一下您将如何更改 C++ 语言以使新语言更加友好。随意陈述新文章和文章的问题。我希望我能找到文章链接,但我记得它很长,是由(IIRC)一所知名学校的教授写的。

4

8 回答 8

6

我看不出有任何理由new用其他东西替换关键字(并且似乎是 C++ 委员会同意我的观点)。它很清楚,并做出了它应该做的事情。您可以在您的类中覆盖operator new,无需使用定义。

要消除new[]/delete[]问题,您可以使用std::vector.

如果你想使用智能指针,你可以使用它,但我想控制何时使用智能指针。这就是为什么我喜欢它在 C++ 中的工作方式——能够控制低级细节的高级行为。

于 2009-08-13T07:08:37.983 回答
3

问题匹配 new、delete、new[]、delete[]

没什么大不了的。
您应该将内存分配包装在一个类中,因此这不会真正影响普通用户。单个对象可以用智能指针包装。而数组可以用 std::Vector<> 表示

不能像使用 malloc 一样使用 #define 和 new。

像这样搞乱 malloc 的原因是在您的应用程序和标准内存管理层之间引入您自己的内存管理层。这是因为在 C 语言中不允许您编写自己的 malloc 版本。在 C++ 中,编写自己的 new 版本是非常合法的,这使得这个技巧变得不必要。

于 2009-08-13T07:24:04.753 回答
2

我会给它newC# 中的语义(或多或少):

  1. 为对象分配内存。
  2. 通过将成员变量设置为其默认值来初始化内存(通常为 0 代表值,null代表引用)。
  3. 初始化对象的动态绑定机制(C++ 中的 vtables,托管 VM 的类型 def 表)。
  4. 调用构造函数,此时虚拟调用按预期工作。
  5. 对于没有垃圾收集的语言(此时是新语言的 eww),从调用中返回 asmart_ptr或类似的。

此外,将所有对象设为值类型或引用类型,因此您不必保留显式的smart_ptr. 只允许new为引用类型进行堆分配,并确保它包含正确调用析构函数的信息。对于值类型,new从堆栈调用内存上的构造函数。

于 2009-08-13T07:07:47.813 回答
1

使用垃圾收集,这样您就无需将新的东西与任何东西相匹配。

于 2009-08-13T07:05:48.457 回答
1

通过使用 STL 容器类和各种 boost:smart_ptrs,几乎不需要在 C++ 代码中显式调用 new 或 delete。

您可能需要调用 new 的几个地方(例如,初始化智能指针)使用命名构造器惯用语来返回包装在例如a中的类类型指针boost:shared_ptr

但是 C++ 和 STL 非常努力地允许您将大多数对象视为值对象,因此您可以构造对象而不是指针并直接使用它们。

考虑到这一切,几乎不需要替换 new 运算符——这样做会引入许多问题,无论是需要垃圾收集器,还是减少 C++ 为程序员提供的精细低级控制。

于 2009-08-13T07:20:43.027 回答
0

如果您的语言被垃圾回收,您可以避免使用new关键字。这就是 Python 所做的(而 Lisp 几乎在 5 年前就做到了!)。另请参阅 Peter Norvig 提供的类似问题的答案。(没有“新闻”是好消息吗?)

于 2009-08-13T07:28:23.090 回答
0

有时您想用工厂替换构造函数。这是一个众所周知的重构。用工厂方法替换构造函数。所以也许这就是这篇文章的意思?

顺便说一句,您经常会看到直接调用 new 被替换为Factory Method

Unity等 DI 框架将这一概念提升到了另一个层次。正如您在以下 C# 代码中看到的那样,没有应用“new”来创建 IMyClass 接口:

IUnityContainer myContainer = new UnityContainer();
myContainer.RegisterType<IMyClass, SomeClass>();
IMyClass thing = myContainer.Resolve<IMyClass>();
于 2009-08-13T07:29:22.323 回答
0

C++ 有一个单独的new运算符(或 C malloc)的原因主要是为了创建生命周期超过创建它们的函数范围的对象。

如果您有尾调用消除和延续,您不会在意 - 对象都可以在堆栈上创建并且具有无限范围 - 一个对象可以存在,直到您调用与超出范围并被破坏的对象相对应的延续. 然后,您可能需要一些东西来进行垃圾收集或以其他方式压缩堆栈,以免它充满不再需要的对象( Chicken Scheme 和 TinyOS 2 是两个不同的示例,用于在运行时或编译时间;Chicken Scheme 不允许 RAII,TinyOS 不允许真正的动态分配),尽管对于大量代码,这样的方案与 RAII 并没有太大的不同,因为它可以选择更改顺序对象被破坏。

于 2009-08-13T09:01:50.470 回答