C ++ оператор перегрузки (), lvalue и rvalue - PullRequest
0 голосов
/ 19 мая 2019

Рассмотрим следующий простой класс.

#include <iostream>

using namespace std;

class test
{
public:
  int* myvar;
  int sz;

  test()
  {
    sz = 10;
    myvar = new int[10];
  }

  void dump()
  {
    for(int i = 0; i < sz; i++)
    {
      cout << myvar[i] << " ";
    }
    cout << endl;
  }

  int& operator()(int index)
  {
    if(index >= sz)
    {
      int* newvar = new int[index+1];

      for(int i = 0; i < sz; i++)
      {
        newvar[i] = myvar[i];
      }
      sz = index+1;
      delete myvar;
      myvar = newvar;
    }
    return myvar[index];
  }

  const int operator()(int index) const
  {
    if(index >= sz)
    {
      throw "index exceeds dimension";
    }
    else
    {
      return myvar[index];
    }
  }
};

Он должен вести себя как динамический массив.Я перегружен оператором ().Моя идея заключалась в том, что для присваивания (lvalue) будет вызываться верхняя версия (), а для операции «только для чтения» (rvalue) используется нижняя версия ().Пример кода должен более четко объяснить, что я имею в виду:

int main()
{
  test x;

  // will give 10 times zero
  x.dump();

  // assign some values
  x(1) = 7;
  x(9) = 99;

  // will give
  // 0 7 0 0 0 0 0 0 0 99
  x.dump();

  // should give 7
  cout << x(1) << endl;

  // should give 99
  cout << x(9) << endl;

  // this will increase the size of myvar to 15 elements and assign a value
  x(15) = 15;

  // this should give
  // 0 7 0 0 0 0 0 0 0 99 0 0 0 0 0 15
  x.dump();

  // this should throw an exception because x(20) got never assigned a value!
  // but instead of calling the lower version of operator() it also calls the
  // upper, resulting in x being expanded now to 21 elements.
  cout << x(20) << endl;

  // will give 21 elements, instead of 16.
  x.dump();

  return 0;
}

Поэтому я получаю доступ к содержимому myvar через оператор ().Должна быть возможность назначить значение только для любого элемента, но не должно быть возможности запрашивать значение элемента, которое никогда не было установлено ранее.Я подумал, используя разные версии (), одной из которых должно быть const, но очевидно, что компилятор всегда использует верхнюю версию моего оператора, а не нижнюю.Как я могу решить эту проблему?

Я читал о прокси-объекте, например, здесь , но я думаю, что эта реализация не будет работать в моем случае, потому что я использую массив.Итак, а) возможно ли это без прокси-сервера или, если нет, б) как должен выглядеть прокси-сервер в моем случае?

1 Ответ

0 голосов
/ 24 мая 2019

Итак, вот решение, которое я наконец-то нашел (вроде):

#include <iostream>

using namespace std;

template <class T> class myclass
{
private:
  unsigned numel;
  T* elem;

public:

  class proxy
  {
    private:

        T*& elem;
        unsigned& numel;
        const unsigned index;

        proxy(T*& elem, unsigned& numel, unsigned index) : elem(elem), numel(numel), index(index) { }

        // didn't really need those two
        // proxy(const proxy&) = default;
        // proxy(proxy&&) = default;

        friend class myclass;

    public:
        proxy& operator=(const T& value)
        {
          if(index >= numel)
          {
            cout << "assignment to an element outside the range!" << endl;
            cout << "old size: " << numel << endl;
            cout << "new size: " << index+1 << endl << endl;

            T* newelem = new T[index+1];
            for(unsigned i = 0; i <= index; i++)
            {
              if(i < this->numel)
              {
                newelem[i] = this->elem[i];
              }
              else
              {
                newelem[i] = 0;
              }
            }

            if(this->elem != nullptr)
            {
              delete this->elem;
            }
            this->elem = newelem;
            this->numel = index+1;
          }

          this->elem[index] = value;
          return *this;
        }

        proxy& operator=(const proxy &other)
        {
          *this = (const T&)other;
          return *this;
        }

        operator T&()
        {
          if(index >= numel)
          {
            cout << "cannot query the value of elements outside the range!" << endl;
            cout << "# of elements: " << numel << endl;
            cout << "index requested: " << index << endl << endl;

            throw out_of_range("");
          }
          return elem[index];
        }

        operator const T&() const
        {
          if(index >= numel)
          {
            throw out_of_range("");
          }
          return elem[index];
        }
    };

  myclass() : numel(0), elem(nullptr) {};

  myclass(unsigned count)
  {
    this->numel = count;
    this->elem = new T[count];
  }

  ~myclass()
  {
    if(this->elem != nullptr)
    {
      delete this->elem;
    }
  }


  friend ostream& operator<<(ostream& os, const myclass& mc)
  {
    os << endl;
    for(unsigned i = 0; i < mc.numel; i++)
    {
      os << mc.elem[i] << "  ";
      os << endl;
    }
    os << endl;
    return os;
  }

  proxy operator()(unsigned index)
  {
    return proxy(this->elem, this->numel, index);
  }

};


int main()
{
  myclass<double> my;

  my(1) = 77;
  my(0) = 200;
  my(8) = 12;

  cout << my;

  try
  {
    cout << my(0) << endl;
    cout << my(1) << endl;
    cout << my(8) << endl;
    cout << my(10) << endl;
  }
  catch(...)
  {
    cout << "error catched" << endl << endl;
  }

  my(10) = 10101;

  cout << my(10) << endl;

}

вывод на терминал выглядит так:

assignment to an element outside the range!
old size: 0
new size: 2

assignment to an element outside the range!
old size: 2
new size: 9


200  
77  
0  
0  
0  
0  
0  
0  
12  

200
77
12
cannot query the value of elements outside the range!
# of elements: 9
index requested: 10

error catched

assignment to an element outside the range!
old size: 9
new size: 11

10101
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...