传递对象与传递引用/指针

Passing objects vs. Passing references/pointers?

本文关键字:指针 对象 引用      更新时间:2023-10-16

因此,正如我们都希望知道的那样,在面向对象编程中,当你需要以某种方式访问另一个类的方法中的一个类实例时,你会转向通过参数传递该实例。

我很好奇,在传递对象或指向该对象的指针时,良好的实践/不太容易破坏东西有什么区别?

养成通过引用传递对象的习惯。

void DoStuff(const vector<int>& values)

如果需要修改原始对象,请省略const限定符。

void DoStuff(vector<int>& values)

若您希望能够接受一个空/无答案,请通过指针传递它。

void DoStuff(vector<int>* values)

如果您想对本地副本执行某些操作,请按值传递。

void DoStuff(vector<int> values)

只有当引入大量并发时,问题才会真正出现。到那时,你会知道什么时候不使用某些传球技术。

如果您希望能够指示不存在(通过传递NULL),则传递一个指向对象的指针。

尽量不要传递对象的值,因为这会调用复制构造函数在调用函数的范围内创建对象的本地版本。相反,通过引用传递。然而,这里有两种模式。为了在没有开销的情况下获得完全相同的传递值(不可变的"副本")的有效行为,请传递const引用。如果您觉得需要更改传递的对象,请通过(非常数)引用传递。

我选择const引用作为默认值。当然,如果必须为客户端更改对象,则使用非const。很少要求偏离使用参考文献。

指针不是很像C++,因为引用是可用的。引用是很好的,因为它们被禁止引用任何内容Update:为了澄清,首选用于类型和数组的适当容器,但对于某些内部实现,您需要传递一个指针。

对象/值在语义上完全不同。如果我需要一个副本,我通常只会在需要的功能中创建它:

void method(const std::string& str) {
  std::string myCopy(str);
  ...

事实上,您可以传递给方法的是指向对象的指针、对对象的引用和对象的副本,所有这些也可以是常量。根据你的需要,你应该选择最适合你需要的。

你可以做的第一个决定是,你传递的东西是否应该能够在你的方法中改变。如果你不打算更改它,那么const引用可能是最好的选择(不更改也意味着你不打算调用该对象的任何非const方法)。这样做有什么好处?编译对象的安全时间以及方法签名本身都会说"我不会更改那个参数"。

如果你必须更改这个对象,你可以传递一个引用或一个指向它的指针。不是必须只选择其中一个选项,这样你就可以选择其中任何一个。我能想到的唯一区别是指针可以是NULL(即根本不指向任何对象),而引用总是指向现有对象。

如果您的方法中需要的是对象的副本,那么您应该传递对象的副本(而不是引用和指针)。例如,如果你的方法看起来像

void Foo(const A& a) {
  A temp = a;
}

这清楚地表明,传递副本是一个更好的选择。

希望这能让事情变得更清楚一点。

实际上,除非您想以某种方式指示不存在对象,否则没有充分的理由将指针传递给对象。

如果要更改对象,请将引用传递给它。如果要保护它不受函数内更改的影响,请按值或至少const引用传递。

有些人通过引用来提高速度(例如,只传递一个大型结构的地址,而不是结构本身),但我不同意这一点。在大多数情况下,我更喜欢我的软件安全而不是快速,这是一句话的必然结果:"你的优化程度不能低于错误":-)

面向对象编程是关于多态性,Liskov替换原则,旧代码调用新代码,你能想到的。将一个具体的(派生的)对象传递给一个处理更抽象(基础)对象的例程。如果你不这样做,你就不是在做OOP。

这只有在传递引用或指针时才能实现。传递值最好是为值保留的。

区分值和对象非常有用。价值观总是具体的,没有多态性。它们往往是不可变的。5是5,"abc"是"abc"。您可以通过值或通过(const)引用来传递它们。

对象在某种程度上总是抽象的。给定一个对象,几乎总是可以将其细化为更具体的对象。矩形区域可以是可绘制的,可以是窗口,可以是ToplevelWindow,可以是ManagedWindow…这些必须通过引用传递。

指针是一个完全独立的蠕虫罐头。根据我的经验,最好避免裸指针。使用不能为NULL的智能指针。如果需要可选参数,请使用显式optional类模板,如boost::optional