C++关于传递对象(指针与引用)的约定

C++ conventions regarding passing objects (pointer vs reference)

本文关键字:引用 约定 指针 于传递 对象 C++      更新时间:2023-10-16

我想制定将参数传递给函数/方法的约定。我知道这是一个常见的问题,并且已经回答了很多次,但我搜索了很多,没有发现任何完全让我满意的东西。

按价值传递是显而易见的,我不会提到这一点。我想出的是:

  1. 通过非常量引用方式传递,该对象被修改
  2. 通过常量引用手段传递,该对象被使用
  3. 通过指针传递意味着将对对象的引用存储。是否传递所有权将取决于上下文。

它似乎是一致的,但是当我想选择堆分配的对象并将其传递给 2. case 参数时,它看起来像这样:

void use(const Object &object) { ... }
//...
Object *obj = getOrCreateObject();
use(*obj);

Object &obj = *getOrCreateObject();
use(obj);

在我看来,两者都很奇怪。你有什么建议?

PS 我知道应该避免原始指针并使用智能(更容易的内存管理和所有权的表现力),这可能是重构我所从事的项目的下一步。

如果您愿意,可以使用这些约定。但请记住,在处理其他人编写的代码时,您不能假设约定。您也不能假设阅读您的代码的人知道您的约定。当界面可能不明确时,您应该使用注释来记录它。

通过指针传递意味着,该对象将被存储。谁是它的主人将取决于上下文。

我只能想到一个上下文,其中指针参数的所有权应该转移到被调用者:智能指针的构造函数。

除了可能的存储意图之外,指针参数还可以具有与引用参数相同的含义,但该参数是可选的。通常不能使用引用表示可选参数,因为它们不能为 null - 尽管对于自定义类型,您可以使用对 sentinel 值的引用。

在我看来,两者都很奇怪。你有什么建议?

对我来说,两者都不奇怪,所以我的建议是习惯。

约定的主要问题是,您没有考虑到与不遵循约定的代码(例如由其他人编写)接口的可能性。

一般来说,我使用一组不同的约定,很少发现需要绕过它们。 (主要的例外是如果需要使用指向指针的指针,但我很少需要直接这样做)。

如果以下任何一项可能为真,则通过非const引用是合适的;

  • 对象可能会更改;
  • 该对象可以通过非const引用传递给另一个函数[当选择省略const的开发人员使用第三方代码时相关 - 这实际上是许多初学者或懒惰的开发人员都会做的事情];
  • 该对象可以通过非const指针传递给另一个函数[当使用第三方代码是选择省略const的开发人员或使用遗留API时相关];
  • 调用对象的非const成员函数(无论它们是否更改对象)[对于希望避免使用const的开发人员使用第三方代码时,也经常考虑]。

相反,如果满足以下所有条件,则可以传递const引用;

  • 不会更改对象的非mutable成员;
  • 对象仅通过引用、const指针或值传递给其他函数const;
  • 仅调用对象的const成员函数(即使这些成员能够更改mutable成员)。

如果函数无论如何都会复制对象,我将按值传递而不是按引用传递const。 (例如,我不会传递const引用,然后在函数中构造传递对象的副本)。

如果传递非constconst引用是合适的,则传递非指针是相关的,但也有可能不传递任何对象(例如nullptr)。

如果传递const引用是合适的,则传递const指针是相关的,但也有可能不传递任何对象(例如nullptr)。

我不会更改以下任何一项的约定

  • 在函数中存储指向对象的引用或指针以供以后使用 - 可以将指针转换为引用,反之亦然。 并且可以存储任何一个(可以分配指针,可以使用引用来构造对象);
  • 区分动态分配的对象和其他对象 - 因为我基本上要么完全避免使用动态内存分配(例如,使用标准容器,并通过引用传递它们,或者只是从它们传递迭代器),或者 - 如果我必须直接使用new表达式 - 将指针存储在另一个负责释放的对象(例如std::smart_pointer),然后传递包含的对象。

在我的观点中,它们是相同的。在文章的第一部分中,您正在谈论签名,但您的示例是关于函数调用的。