使用`new`关键字后,避免持续解除引用的标准方法是什么?

时间:2013-05-11 16:27:22

标签: c++ new-operator dereference

new关键字会将指针指向创建的对象,这意味着您不得不遵守它 - 我只是害怕性能可能会受到影响。

E.g。我面临的一个常见情况:

class cls {
    obj *x; ...
}

// Later, in some member function:
x = new obj(...);
for (i ...) x->bar[i] = x->foo(i + x->baz);  // much dereferencing

我并不过分热衷于引用变量,因为我有很多*x(例如*x, *y, *z, ...)并且必须在每个函数的开头写&x_ref = *x, &y_ref = *y, ...很快变得无聊并且详细。

确实,做得更好:

class cls {
    obj x; ...    // not pointer
}
x_ptr = new obj(...);
x = *x_ptr;       // then work with x, not pointer;

那么使用new创建的变量的标准方法是什么?

3 个答案:

答案 0 :(得分:5)

没有其他方法可以使用new创建的对象。 new创建的未命名对象的位置始终为运行时值。这立即意味着对此类对象的每次访问都将始终无条件地要求解除引用。没有办法绕过它。根据定义,这就是“解除引用”实际上是通过运行时地址进行访问。

通过在函数开头执行&x_ref = *x来尝试“使用引用”替换指针是没有意义的。他们绝对没有成就。在这种情况下,参考文献只是语法糖。它们可能会减少源代码中*运算符的数量(并且可能会增加&运算符的数量),但它们不会影响机器代码中的物理解引用数。它们将导致绝对相同的机器代码包含绝对相同数量的物理解除引用和绝对相同的性能。

请注意,在多次重复引用解除引用的上下文中,智能编译器可能(并且将)实际读取并将目标地址存储在CPU寄存器中,而不是每次从内存中重新读取它。通过存储在CPU寄存器中的地址访问数据总是最快的,即它比通过嵌入CPU指令的编译时地址访问数据更快。因此,重复解除可管理的复杂性可能不会对性能产生任何负面影响。当然,这在很大程度上取决于编译器的质量。

在您发现重复解除引用对性能产生重大负面影响的情况下,您可能会尝试将目标值缓存在本地缓冲区中,使用本地缓冲区进行所有计算,然后在结果准备就绪时将其存储在原始指针。例如,如果您有一个通过指针int *px重复访问(读取和/或写入)数据的函数,您可能希望将数据缓存在普通的局部变量x

int x = *px;

在整个功能中使用x,最后执行

*px = x;

毋庸置疑,这只有在复制对象的性能影响较小时才有意义。当然,在别名情况下你必须小心这些技术,因为在这种情况下,*px的值不会持续保持。 (再次注意,在这种情况下,我们使用普通变量x,而不是引用。您尝试用引用替换单级指针实际上什么都没有。)

同样,这种“数据兑现”优化也可以由编译器隐式执行,假设编译器很好地理解代码中存在的数据别名关系。这就是C99风格的restrict关键字可以帮助它的地方。但这是一个不同的话题。

无论如何,没有“标准”方法可以做到这一点。最好的方法主要取决于您对代码中每个特定部分中存在的数据流关系的了解。

答案 1 :(得分:3)

不使用new关键字实例化对象,如下所示:

obj x;

或者obj的构造函数是否带参数:

obj x(...);

这将为您提供一个对象而不是指针。

答案 2 :(得分:2)

您必须决定是要在堆上还是在堆栈上分配您的东西。这完全取决于您的要求。并且解除引用不会降低性能。您可以在堆中分配cls,这将超出范围并将obj的实例保留在堆栈中

class cls {
  obj x;//default constructor of obj will be called
}

如果obj没有默认构造函数,则需要在cls构造函数中调用适当的构造函数