【问题标题】:How does operator[] work in C++ (Overloading on user defined objecs)?operator[] 如何在 C++ 中工作(重载用户定义的对象)?
【发布时间】:2014-12-23 07:27:24
【问题描述】:

我了解开发人员能够重载 operator[] 以获取或设置某些内容,具体取决于操作员在哪一侧。例如,您可以使用obj[2] = test_obj;test_obj = obj[2]。但是我不明白第一个示例是如何工作的; operator[] 返回对堆上某物的引用,所以当 obj[2] = test_obj; 运行时,发生了什么?如果索引 2 已经有东西,它会被删除吗? 2处的对象的析构函数首先被调用吗?由于operator[] 的方法体中没有进行此类检查,这一切是如何完成的?

【问题讨论】:

  • 这一切都由参考机制处理。看看这些,你就会明白这一点。
  • operator[] 返回一个引用是的,但如果它在堆上,那只是巧合。它也可以在堆栈上,或内存中的任何其他任意位置。删除通常不是问题

标签: c++ arrays vector operator-overloading


【解决方案1】:

引用的工作方式与指针非常相似,但引用总是指向某个实际值。

因此,当operator[] 返回一个引用并且您分配给该引用时,您将直接分配给该引用所引用的值。在您的示例中,这将是位于索引 2 处的对象。请注意,Container::operator[] 的返回类型不是对 Container 的引用,而是通常是对包含值的引用。

要给出详细答案,这取决于所讨论类型的operator[] 的实际返回类型。如果返回类型是对原始类型的引用,那么它就像对原始类型的任何其他引用一样工作。不涉及析构函数。赋值直接在引用的值中进行。例如:

int x = 5;
x = 6;

不调用析构函数。不调用任何运算符。 x 包含的值被赋值运算符右侧的值替换。

如果涉及到引用,则类似:

int x = 5;
int &r = x;
r = 6;

不调用析构函数。不调用任何运算符。 r 指向的值(在本例中为 x)被赋值运算符右侧的值替换。

同样,如果涉及非原始类型,则将调用给定类型的operator=

Foo x;
Foo y;
// Foo::operator=(const Foo &) is called on x with an argument referencing y
x = y;

xy 的析构函数都不会被调用。

与参考相同:

Foo x, y;
Foo &r = x;
// Foo::operator=(const Foo &) is called on x with an argument referencing y
r = y;

但是,operator[] 不是必需 来返回引用;不过,强烈建议这样做。

对于非引用或对非原始类型的引用,调用返回值的operator=,因此赋值运算符的行为也很重要。

operator= 的情况下,左侧的析构函数未被调用,但赋值运算符本身负责任何清理工作。这就是为什么赋值运算符通常根据copy-swap idiom 来实现。

所以对于std::vector<int>std::vector<int>::operator[] 返回一个int &。分配发生在参考点所在的位置。

对于std::vector<Foo>std::vector<Foo>::operator[] 返回一个Foo & 并调用Foo::operator=(const Foo &)

然而,就像我提到的,一个类可以从operator[] 返回一个任意对象,并且该对象的赋值运算符将被调用。这可用于通过另一个对象代理分配。您可能会在一个返回 Row 对象的 Matrix 类中看到这一点,该对象又可能实现operator[]。同样,不一定推荐这种设计,但并非闻所未闻。在这些情况下,返回类型可能根本不是引用,而是实际值。然而,它仍然以同样的方式工作Row::operator=Proxy::operator= 负责分配和清除任何必要的资源。这就是Rule of Three 如此重要的原因。

【讨论】:

  • 这是我一直在寻找的答案,但我已经接受了之前的答案,该答案在答案的 cmets 部分得到了进一步说明。
【解决方案2】:

由于operator[] 通常返回一个引用,它的工作原理与引用在左侧的情况完全相同:

Foo& j;
j = test_obj;

引用基本上与左侧的常规变量相同,因为引用基本上只是引用变量的另一种方式。所以本质上是一样的:

Foo j;
j = test_obj;

所以它基本上只是copy assignment,使用Foo::operator=(Foo const&)(在C++中总是存在的)。 (即使它被删除了,它仍然存在——如果你试图调用它说它被删除,而不是不存在函数,你会得到一个错误。)

【讨论】:

  • 请解释反对意见,以便更正或改进答案。
  • 但这并没有回答它如何知道删除已经存在的内容。例如,如果它返回了对一个在堆上动态分配的对象的引用,编译器是否会先自动删除它,然后在堆上为 rhs 上的对象动态分配新空间并调用其构造函数?这些都不是在 operator[] 的主体中,所以它是在幕后发生的吗?
  • 不,这些事情都不会自动发生。如果有需要释放的东西,operator[] 不会为你释放它。
  • @Akash 是的,返回对象的赋值运算符不是容器。
  • @Akash 是的,operator[] 返回一个引用。如果该引用位于赋值运算符的左侧,则引用引用的变量的值已更改。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-03-31
  • 1970-01-01
  • 2021-01-17
  • 1970-01-01
  • 2012-11-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多