为模板Polynom类重载operator []

时间:2011-11-09 09:20:04

标签: c++ operator-overloading square-bracket

我正在编写一个模板Polynom<T>类,其中T是其系数的数字类型。

多项式的系数存储在std::vector<T> coefficients中,其中coefficients[i]对应于实数多项中的x^i。 (因此x的幂次数递增)。

保证coefficients向量始终包含至少一个元素。 - 对于零多项式,它是T()

我想重载operator[]来执行以下操作:

  1. 传递给operator []的索引对应于我们想要修改/读取系数的X的幂。
  2. 如果用户想要读取系数,则应该抛出负数指数,为存储范围内的指数返回coefficients.at(i) - 并且合理地返回0表示所有其他指数,而不是抛出。
  3. 如果用户想要修改系数,它应该抛出负指数,但让用户自由修改所有其他指数,即使指定的索引大于或等于{{1} }。所以我们想以某种方式调整矢量大小。
  4. 我遇到的主要问题如下:

    1

    如何区分读取案例和写入案例?有一个人在没有解释的情况下离开了我,但说写了两个版本:

    coefficients.size()

    不够。但是,我认为编译器更喜欢在读取情况下的const版本,不是吗?

    2

    我想确保const T& operator[] (int index) const; T& operator[] (int index); 向量中没有存储任何尾随零。所以我必须事先知道,“之前”我返回了我的系数的可变coefficients,用户想要分配的值。而且我知道T&没有得到第二个论点。

    显然,如果这个值不是零(不是T()),那么我必须调整我的向量大小并将适当的系数设置为传递的值。

    但是我不能事先做到(在从operator[]返回T&之前),因为如果要分配的值是T(),那么,如果我提前调整了系数向量,它最终会有很多尾随的“零”。

    当然,我可以在类的每个其他函数中检查尾随零,并在这种情况下删除它们。对我来说似乎是一个非常奇怪的决定,我希望每个函数都开始工作,假设它的大小&gt;在向量的末尾没有零。 1。

    你能否告诉我这个问题的具体解决方案? 我听说过一些内容类可以隐式转换为operator[]并且重载T&,但我缺乏细节。

    非常感谢你!

5 个答案:

答案 0 :(得分:3)

您可以尝试一种选择(我没有测试过):

template<typename T>
class MyRef{
private:
   int index;
   Polynom<T>*p;
public:
    MyRef(int index, Polynom<T>*p) : index(index), p(p) { }

    MyRef<T>& operator=(T const&t); //and define these appropriately
    T operator T() const;         
};

并定义:

    MyRef<T> operator[](int index){
        return MyRef<T>(index, this);
    }

这样,当您为“引用”赋值时,它应该可以访问多项式中所有需要的数据,并采取适当的操作。

我对你的实现不够熟悉,所以我将举一个非常简单的动态数组的例子,其工作原理如下:

  • 您可以毫无顾虑地阅读任何int index;以前没有写过的元素应该读作0;
  • 当您写入当前分配的数组末尾之后的元素时,会重新分配该元素,并将新分配的元素初始化为0
#include <cstdlib>
#include <iostream>
using namespace std;

template<typename T>
class my_array{
private:
    T* _data;
    int _size;

    class my_ref{

        private:
            int index;
            T*& obj;
            int&size;
        public:
            my_ref(T*& obj, int&size, int index)
                : index(index), obj(obj), size(size){}

            my_ref& operator=(T const& t){

                if (index>=size){    
                    obj = (T*)realloc(obj, sizeof(T)*(index+1) );
                    while (size<=index)
                        obj[size++]=0;
                }
                obj[index] = t;

                return *this;
            }

            //edit:this one should allow writing, say, v[1]=v[2]=v[3]=4;
            my_ref& operator=(const my_ref&r){              
                operator=( (T) r);
                return *this;
            }

            operator T() const{
                return (index>=size)?0:obj[index];
            }

    };

public:
    my_array() : _data(NULL), _size(0) {}

    my_ref operator[](int index){
        return my_ref(_data,_size,index);
    }

    int size() const{ return _size; }

};

int main(){

    my_array<int> v;

    v[0] = 42;
    v[1] = 51;
    v[5] = 5; v[5]=6;
    v[30] = 18;

    v[2] = v[1]+v[5];
    v[4] = v[8]+v[1048576]+v[5]+1000;

    cout << "allocated elements: " <<  v.size() << endl;
    for (int i=0;i<31;i++)
        cout << v[i] << " " << endl;

    return 0;
}

这是一个非常简单的例子,目前的形式并不是很有效,但它应该证明这一点。

最后,您可能希望重载operator&以允许*(&v[0] + 5) = 42;之类的内容正常工作。对于此示例,您可以让operator&提供my_pointer来定义operator+以对其index字段进行算术运算并返回新的my_pointer。最后,您可以重载operator*()以返回my_ref

答案 1 :(得分:2)

对此的解决方案是代理类(未经测试的代码如下):

template<typename T> class Polynom
{
public:
   class IndexProxy;
   friend class IndexProxy;
   IndexProxy operator[](int);
   T operator[](int) const;
   // ...
private:
   std::vector<T> coefficients;
};

template<typename T> class Polynom<T>::IndexProxy
{
public:
  friend class Polynom<T>;
  // contrary to convention this assignment does not return an lvalue,
  // in order to be able to avoid extending the vector on assignment of 0.0
  T operator=(T const& t)
  {
    if (theIndex >= thePolynom.coefficients.size())
      thePolynom.coefficients.resize(theIndex+1);
    thePolynom.coefficients[theIndex] = t;
    // the assignment might have made the polynom shorter
    // by assigning 0 to the top-most coefficient
    while (thePolynom.coefficients.back() == T())
      thePolynom.coefficients.pop_back();
    return t;
  }
  operator T() const
  {
    if (theIndex >= thePolynom.coefficients.size())
      return 0;
    return thePolynom.coefficients[theIndex];
  }
private:
  IndexProxy(Polynom<T>& p, int i): thePolynom(p), theIndex(i) {}
  Polynom<T>& thePolynom;
  int theIndex;
}

template<typename T>
  Polynom<T>::IndexProxy operator[](int i)
  {
    if (i < 0) throw whatever;
    return IndexProxy(*this, i);
  }

template<typename T>
  T operator[](int i)
{
  if (i<0) throw whatever;
  if (i >= coefficients.size()) return T();
  return coefficients[i];
}

显然,上面的代码没有得到优化(特别是赋值运算符显然有优化空间)。

答案 2 :(得分:1)

您无法区分具有运算符重载的读取和写入。您可以做的最好的事情是区分const设置中的使用情况和非const设置,这是您的代码段所做的。所以:

Polynomial &poly = ...;

poly[i] = 10;  // Calls non-const version
int x = poly[i];  // Calls non-const version

const Polynomial &poly = ...;

poly[i] = 10;   // Compiler error!
int x = poly[i]  // Calls const version

因此,对于您的两个问题,答案似乎都是单独的setget函数。

答案 3 :(得分:1)

我看到了两个问题的解决方案:

  1. 而不是将系数存储在std::vector<T>中,而不是将它们存储在std::map<unsigned int, T>中。这样您将只存储非零系数。您可以创建自己的基于std::map的容器,该容器将消耗存储在其中的零。这样,您还可以为x ^ n格式的多项式保存一些存储,其中包含大n。

  2. 添加一个内部类,用于存储索引(幂)和系数值。您将从operator[]返回对此内部类的实例的引用。内部类将覆盖operator=。在重写的operator=中,您将获取存储在内部类实例中的索引(幂)和系数,并将它们刷新到存储系数的std::vector

答案 4 :(得分:0)

这是不可能的。我能想到的唯一方法是提供一个特殊的成员函数来添加新的系数。

编译器通过查看const的类型来决定const和非Polynom版本,而不是通过检查对返回值执行的操作类型。