Оператор перегрузки [] для шаблона класса Polynom - PullRequest
2 голосов
/ 09 ноября 2011

Я пишу шаблон Polynom<T> class, где T - числовой тип его коэффициентов.

Коэффициенты полинома хранятся в std::vector<T> coefficients, где coefficients[i] соответствует x^i в реальном полином.(таким образом, степени х в порядке возрастания).

Гарантируется, что вектор coefficients всегда содержит хотя бы один элемент.- для нулевого полинома это T().

Я хочу перегрузить operator[], чтобы сделать следующее:

  1. Индекс, переданный оператору [], соответствуетСтепень 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& из operator[]), потому что если назначаемое значение равно T (), то при условии, что я изменю свой вектор коэффициентов взаранее, в конце концов, у него будет много конечных «нулей».

Конечно, я могу проверить наличие конечных нулей в любой другой функции класса и удалить их в этом случае.Мне кажется очень странным решение, и я хочу, чтобы каждая функция начинала работать в предположении, что в конце вектора нет нулей, если его размер> 1.

Не могли бы вы посоветовать мне какКакое конкретное решение этой проблемы?Я что-то слышал о написании внутреннего класса, неявно преобразуемого в T& с перегруженным operator=, но мне не хватает деталей.

Большое спасибо заранее!

Ответы [ 5 ]

3 голосов
/ 09 ноября 2011

Один вариант, который вы можете попробовать (я не проверял это):

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.

2 голосов
/ 09 ноября 2011

Решением этого является прокси-класс (непроверенный код следует):

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];
}

Очевидно, что приведенный выше код не оптимизирован (особенно у оператора присваивания явно есть место для оптимизации).

1 голос
/ 09 ноября 2011

Я вижу два решения вашей проблемы:

  1. Вместо сохранения коэффициентов в std::vector<T> сохраните их в std::map<unsigned int, T>. Таким образом, вы будете хранить только ненулевые коэффициенты. Вы можете создать свой собственный контейнер на std::map, который будет использовать нули, хранящиеся в нем. Таким образом, вы также экономите место для полиномов вида x ^ n с большими n.

  2. Добавить внутренний класс, который будет хранить индекс (мощность) и значение коэффициента. Вы бы вернули ссылку на экземпляр этого внутреннего класса из operator[]. Внутренний класс будет перезаписывать operator=. В переопределенном operator= вы бы взяли индекс (мощность) и коэффициент, сохраненные в экземпляре внутреннего класса, и сбросили их в std::vector, где вы храните свои коэффициенты.

1 голос
/ 09 ноября 2011

Вы не можете различить чтение и запись с перегрузками операторов. Лучшее, что вы можете сделать, - это различить использование в настройках 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

Похоже, что ответ на оба ваших вопроса состоит в том, чтобы иметь отдельные функции set и get.

0 голосов
/ 09 ноября 2011

Это невозможно.Единственный способ, о котором я могу подумать, - это предоставить специальную функцию-член для добавления новых коэффициентов.

Компилятор выбирает версию const и не- const, рассматривая тип Polynomи не проверяя, какая операция выполняется с возвращаемым значением.

...