【问题标题】:Why are pointers to the base class preferred over references?为什么指向基类的指针优于引用?
【发布时间】:2012-05-19 23:07:06
【问题描述】:

通常,在 C++ 中,将参数传递给多态对象的函数的代码使用指向它的指针似乎更可取:

class Base {};
class DerivedA : public Base {};
class DerivedB : public Base {};

void OperateOnObject(Base* obj) {};

vector<Base*> objects;
objects.push_back(new DerivedA());
objects.push_back(new DerivedB());

for (size_t i=0; i<objects.size(); i++) {
  OperateOnObject(objects[i]);
}

为什么OperateOnObject() 通常被写入指向Base 的指针而不是引用?切片是否存在潜在问题? (例如 vtable 丢失了)

我看到this response 提出了一个类似的问题,但它似乎没有解决同样的问题。

【问题讨论】:

  • 在我从事的所有项目中,指针与引用的偏好始终取决于多态性以外的其他因素......
  • 一个额外的因素是,在 VisualStudio 中,与引用相比,您似乎获得了更多的指针调试信息(例如,最派生类型的类型信息),因此这也是首选的一个因素另一个
  • 在良好的设计中,IDE/调试器的(不)能力不应影响设计的基本属性。
  • 确实如此,但所有其他条件都相同......
  • 它们很少相等,通常您有其他要求来弥补一个或另一个方向,例如nullptr 或设计策略的必要性,它说使用指针来表示所有权等的转移等等。

标签: c++ pointers reference polymorphism


【解决方案1】:

切片当然没有问题 --- 你的类不需要 vtable,因为它们没有虚函数,但即使它们确实有它们,传递引用也不会复制对象,因此不会切任何东西。

您可以像通过指针一样通过引用进行虚拟调用。据我所知,没有任何实际原因,只是为了风格。

也许这与多态对象通常由带有new 的指针创建这一事实有关,当然必须通过指针(或智能指针)存储在容器中,因为你不能有一个参考容器。这样看来,总是通过指针来操作它们似乎更加一致。

另外,如果你想在像for_each这样的标准算法中使用OperateOnObject,那么它要么必须接受容器的元素类型,即指针,否则你必须将它包装在一个函数中执行取消引用的适配器。标准 C++ 没有那个适配器,所以在它的基础上建立你的风格是一个麻烦的世界。

相关:看看如果OperateOnObject 接受一个引用,然后你使用迭代器迭代你的向量会发生什么:

for (vector<Base*>::iterator i = objects.begin(); i != objects.end(); ++i) {
    OperateOnObject(**i);
}

第 20 次双重间接让你烦恼或困惑时,你将更改 OperateOnObject 的签名;-)

顺便说一句,一些样式指南警告不要传递非 const 引用,无论该类型是否是多态基类,因为您无法立即区分 pass-by-reference 和 pass-by-在调用站点阅读代码时的价值。所以他们更喜欢OperateOnObject 无论如何都要拿一个指针。

我个人认为这个论点有点弱——一个函数的名称应该大致告诉你它做了什么,特别是像 ChangeTheObject(my_object); 这样的声明并不是那么巧妙地暗示我,因为它没有使用任何返回值,它必须改变它的论点。但我承认,如果你正确地遵循一种风格,那么清楚且一致地将突变器与纯函数区分开来会有一些好处。

【讨论】:

    【解决方案2】:

    引用不会出现切片问题 - 在这方面,它们与指针一样好。没有“硬”的理由偏爱指针,除非您希望 NULL 的语义可用于您的代码(即传递“无”的能力,如果您使用引用则不存在)。

    我认为指针类型通常用作多态函数的参数以匹配集合的语义:由于您无法创建 vector 引用,因此您的代码最终将不得不取消引用从集合中获得的指针,然后再将它们传递给职能。这可能很烦人。

    【讨论】:

    • 好的,这实际上只是一个风格问题,没有技术原因。这似乎与史蒂夫的回应一致
    猜你喜欢
    • 1970-01-01
    • 2015-08-12
    • 2011-05-03
    • 1970-01-01
    • 2012-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-26
    相关资源
    最近更新 更多