3

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

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

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

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

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

我遇到的主要问题如下:

1.

如何区分读情况和写情况?一个人没有解释就离开了我,但说写了两个版本:

const T& operator[] (int index) const;
T& operator[] (int index);

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

2.

我想确保coefficients向量中不会存储任何尾随零。所以我必须提前知道,“在”我返回一个可变T&的系数之前,用户想要分配什么值。而且我知道这operator[]不会收到第二个论点。

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

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

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

你能否建议我尽可能具体地解决这个问题?我听说过编写一个可隐式转换为T&with 重载的内部类operator=,但我缺乏细节。

非常感谢您!

4

5 回答 5

4

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

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_pointerwhich 定义operator+在它的字段上做算术index 并返回一个 new my_pointer。最后,您可以重载operator*()以返回到my_ref.

于 2011-11-09T09:26:29.937 回答
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];
}

显然上面的代码没有优化(尤其是赋值运算符有明显的优化空间)。

于 2011-11-09T09:47:29.620 回答
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功能。

于 2011-11-09T09:27:07.363 回答
1

我看到您的问题的两种解决方案:

  1. 而不是将系数存储在 a 中,而是std::vector<T>将它们存储在 a 中std::map<unsigned int, T>。这样,您将永远只存储非零系数。您可以创建自己的std::map基于容器,该容器将消耗存储在其中的零。这样,您还可以为具有较大 n 的 x^n 形式的多项式节省一些存储空间。

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

于 2011-11-09T09:34:47.250 回答
0

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

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

于 2011-11-09T09:26:20.250 回答