【问题标题】:Vector and []-operator overloading向量和 [] 运算符重载
【发布时间】:2013-05-15 15:43:57
【问题描述】:

我从 std::vector 继承了我的类。现在我想重载 [] 运算符。
当我尝试为我的向量分配一个新值时,例如v[0]=5, 我应该会收到消息OK

这是我的代码(我知道,这没有意义,我只是在玩弄):

#include<vector>
#include<iostream>
class Vec : public std::vector<int> {
public:
    int operator[](int);
};

int Vec::operator[](int i) {
    (*this)[i] = i;
    std::cout << "OK";
    return 123;
}

int main() {
    Vec v;
    v[0]=5;
}

不幸的是,我收到以下错误:

In member function ‘int Vec::operator[](int)’:
error: lvalue required as left operand of assignmen
In function ‘int main()’:
error: lvalue required as left operand of assignment

【问题讨论】:

标签: c++ vector operator-overloading


【解决方案1】:

您需要返回对元素的引用 - 但请注意,即使您这样做了,您也会遇到无限递归 - 您的 operator[] 会调用自己。

无论哪种方式 - 从 std::vector 继承都不是一个好主意。改用合成。

【讨论】:

  • 所以从std::vector继承时没有办法重载[]-操作符?
  • @user1170330 我没这么说。您可以拥有int&amp; operator[](int idx) { return std::vector&lt;int&gt;::operator[](idx); }。只是不推荐。
  • 谢谢! return std::vector&lt;double&gt;::operator[](n);可以写短一点吗?
  • @user1170330 如果你根本不写它,它会做同样的事情。
【解决方案2】:

这个特定的错误是因为您没有返回lvalue,通常定义为可以出现在分配左侧的东西,例如v[0] = 5;。正如其他答案中指出的那样,您还有更多问题,但这是您遇到该错误消息 (a) 的具体问题。

重载索引运算符的正确规范是:

int& operator[] (const int nIndex);

如果您想将其视为lvalue,您必须返回该项目的引用(以便可以对其进行修改)。下面的代码显示了一个修复,尽管在这个简化的例子中所有数组索引都映射到相同的值:

#include <vector>
#include <iostream>

class Vec : public std::vector<int> {
    public:
        int& operator[] (int);    // <-- note the '&'
    private:
        int xyzzy;
};

int& Vec::operator[] (int idx) {  // <-- note the '&'
    std::cout << "OK\n";
    return xyzzy;
}

int main () {
    Vec v;
    v[0] = 5;
    v[1] = 6;
    std::cout << v[22] << '\n';
    return 0;
}

这个的输出是:

OK
OK
OK
6

实际上,您不会将所有索引映射到相同的值,上面的代码只是为了说明正确的函数签名。我没有费心给出一个更完整的例子,因为使用非虚拟析构函数的子类经常会导致非平凡代码出现问题(b)


(a) 子类化std::vector 通常不是一个好主意,因为析构函数不是虚拟的,因此在尝试以多态方式销毁对象时可能会遇到麻烦。

你可能最好使用has-a 关系(你的类包含一个向量)而不是is-a 关系(你继承的地方)。

不幸的是,这意味着您可能必须创建许多从类到底层向量的传递方法(尽管只有您需要的那些),但它会解决析构函数的问题。


(b)(a) :-)

【讨论】:

  • 这是什么v[22]?我可以用任何其他号码替换它。如何存储值?例如。在v[0] 中存储56v[1] 中,以便我可以在main() 中访问它们?
  • @paxdiablo:为什么引入xyzzy而不是调用基地的operator[]? - 似乎没有像它可能的那样有用的示例(鉴于上面的评论,它显然使 user1170330 感到困惑)。
  • @user1170330(和 Tony D),这只是一个展示如何正确重载函数的示例(使用&amp;)。因为我反对重载向量,因为它是非虚拟析构函数,所以给出一个更完整的例子似乎没什么意义,这几乎肯定会给你带来更多的问题。如果您阅读了答案的其余部分,它会告诉您该怎么做 - 使用 has-a 而不是 is-a。另请参阅 Joe 在上面对此问题的评论中提供的链接。
【解决方案3】:

下面的代码说明了如何从vector 基类调用operator[]....

#include <iostream>
#include <vector>

struct Vec : std::vector<int>
{
    int& operator[](int n)
    {
        std::cout << "operator[](" << n << ")\n";
        return std::vector<int>::operator[](n);
    }
};

int main()
{
    Vec v;
    v.push_back(10);
    v.push_back(20);
    v[0] += 5;
    std::cout << v[0] << ' ' << v[1] << '\n';
}

运行时的输出:

operator[](0)
operator[](1)
operator[](0)
15 20

不要把所有关于“不要从 std::vector 继承”的讨论都当回事:你必须不遗余力地使用 std::vector&lt;int&gt;* 删除动态分配的 Vec ,或者做一个意外的按值切片复制——即使那样,如果你添加了数据成员,它可能只会咬你。您应该确保您了解这些风险,然后进行自己的评估,但是对于小型实用程序等。有时从这些类继承是富有成效的......

【讨论】:

  • Tony,事实上,通常只有在您在析构函数中添加需要特殊清理的数据成员时(例如,它们已被new'ed )。所以你是对的,简单的程序通常不需要担心。不幸的是,没有人愿意为简单的程序付钱给我,所以我学会了小心:-)
  • @paxdiablo:嗯,这实际上是关于客户端代码的批量和分布——让维护类及其使用的人员了解设计决策并适当地使用它是否可行。非常同意,在公司环境中,对于任何团队项目,通常不值得考虑这些事情,更不用说向你的同事证明决定的合理性了。但是,有时我会因为工作需要很少的实用程序来进行数据提取和分析而获得丰厚的报酬,而生产力和代码简洁/清晰/移动性是最重要的。
  • 'K,我会给你+1,因为你提供了一种有用的方法来获取基础数据(与我的回答不同),并且至少让 OP 意识到可能存在的问题。跨度>
  • @paxdiablo: 重新添加数据成员 - 绝对需要特殊清理是通过基类指针删除的问题,但即使对于 int、@987654328 而言,切片和无法维护不变量仍然是一个问题@ 等。了解 Liskov 替换原则和在 std::vector&lt;int&gt;&amp;s 上工作的函数将绕过 Vec::operator[] 并错过放置在那里的任何副作用/检查等。在程序员了解所有这些问题之前,不要添加额外的状态是一个很好的经验法则。
猜你喜欢
  • 2013-11-10
  • 2022-01-05
  • 2020-03-09
  • 1970-01-01
  • 1970-01-01
  • 2012-05-04
  • 1970-01-01
  • 2015-07-18
  • 2010-11-26
相关资源
最近更新 更多