Как изменить реализацию функции базового класса возвращаемого объекта при возврате объекта C ++ - PullRequest
0 голосов
/ 19 сентября 2010

У меня есть приложение на C ++ с пользовательским классом ArrayBase, который управляет хранением и доступом к непрерывно выделенной области памяти.У меня есть отдельный класс ItrBase, который используется для доступа к данным в этом ArrayBase.ArrayBase имеет функцию createItr(), которая в настоящее время возвращает объект ItrBase.

Мне нужно расширить ArrayBase, чтобы использовать несколько выделений памяти вместо одного смежного.Я создал EnhancedArray класс для этого.Чтобы этот EnhancedArray был совместим с существующим приложением, его функция createItr() должна возвращать что-то, что работает с новыми множественными выделениями памяти.

Итак, я создал производный класс EnhanceItr для этого,Моя проблема в том, что я не могу найти способ для сотен вхождений кода, подобных этому:

ItrBase anIterator = anArray.createItr();
...
double x = anIterator.getData();

, чтобы использовать функцию EhancedItr getData(), когда anArray является EnhancedArray.

Вот простое приложение, иллюстрирующее мое основное расположение.

#include <iostream>
using namespace std;

class ItrBase {
public:
 ItrBase() { cout << "ItrBase constructor.\n"; };
 ~ItrBase() { cout << "ItrBase destructor.\n"; };
 virtual int vfunc() {return 1;};
};


class EnhancedItr : public ItrBase {
public:
 EnhancedItr() { cout << "EnhancedItr constructor.\n"; };
 ~EnhancedItr() { cout << "EnhancedItr destructor.\n"; };
 int vfunc() {return 0;};
};


class ArrayBase {
public:
 ArrayBase() { cout << "ArrayBase constructor.\n"; };
 ~ArrayBase() { cout << "ArrayBase destructor.\n"; };
 virtual ItrBase & createItr() {cout << "in AB's createItr()\n"; return *new ItrBase(); };
};


class EnhancedArray : public ArrayBase {
public:
 EnhancedArray() { cout << "EnhancedArray constructor.\n"; };
 ~EnhancedArray() { cout << "EnhancedArray destructor.\n"; };
 EnhancedItr & createItr() {cout << "in EA's createItr()\n"; return *new EnhancedItr(); };
};


int main()
{
 ArrayBase ab;
 EnhancedArray ea;

 ItrBase itr = ab.createItr();  
 ItrBase eitr = ea.createItr();  //EnhancedItr assigned to ItrBase

 cout << "ArrayBase's Itr .vfunc(): " << itr.vfunc() <<std::endl;
 cout << "EnhancedArray's Itr .vfunc(): " << eitr.vfunc() <<std::endl;
 return 0;
}

Оба вызова на vfunc() выше возвращают 1, когда я хочу, чтобы второй вызов возвратил 0.

В main() я знаю, что если я изменю типы ItrBase на ItrBase &, я получу требуемые типы возврата, но затем я изменяю свой «существующий» код в сотнях областей и деструкторыИтераторы не называются.

Есть ли другая стратегия, которую я не вижу?

Спасибо.

Ответы [ 4 ]

2 голосов
/ 19 сентября 2010

Конечно, если вам разрешено переписывать ItrBase, вы можете использовать делегирование для передачи всех вызовов функций в класс реализации, который вы удерживаете указателем или ссылкой, чтобы полиморфизм действовал. Это было бы очень похоже на pimpl . И вызывающие абоненты не должны были бы быть написаны вообще, только перекомпилированы.

РЕДАКТИРОВАТЬ: код для тех, кто не знаком с pimpl.

struct ItrBase
{
  struct ItrImpl
  {
    virtual ~ItrImpl(){}
    virtual int vfunc() = 0;
  };

  ItrBase(ItrImpl peer) : m_peer(peer) { cout << "ItrBase constructor.\n"; }
  ~ItrBase() { cout << "ItrBase destructor.\n"; }
  int vfunc() { return m_peer->vfunc(); }
private:
  const unique_ptr<ItrImpl> m_peer;
};

class ArrayBase
{
  struct ItrImpl : public ItrBase::ItrImpl
  {
    virtual int vfunc() { return 0; }
  };

public:
  ArrayBase() { cout << "ArrayBase constructor.\n"; };
  ~ArrayBase() { cout << "ArrayBase destructor.\n"; };
  virtual ItrBase createItr() { cout << "in AB's createItr()\n"; return ItrBase(new ItrImpl); };
};

class EnhancedArray : public ArrayBase
{
  struct ItrImpl : public ItrBase::ItrImpl
  {
    virtual int vfunc() { return 1; }
  };

public:
  EnhancedArray() { cout << "EnhancedArray constructor.\n"; };
  ~EnhancedArray() { cout << "EnhancedArray destructor.\n"; };
  virtual ItrBase createItr() { cout << "in EA's createItr()\n"; return ItrBase(new ItrImpl); };
};
0 голосов
/ 20 сентября 2010

Не будучи в курсе Стандартной библиотеки, я не мог использовать реализацию unique_ptr<>, предложенную Беном Фойгтом.(версия> = 4.3) Я полагаю, что взял его концепцию и реализовал ее с помощью базовых указателей.Отмечая, однако, что эта реализация не безопасна для исключений.ItrImpl объекты могут быть оставлены без восстановления.

Вот мой код.Жаль, что createItr() должен возвращать ItrBase объект, а не указатель, иначе я думаю, что я мог бы заставить auto_ptr<> работать.Вывод во время выполнения программы показывает, что ~ItrBase() вызывается только один раз для каждого экземпляра, но я удивлен, что он не вызывается также при возврате объекта из createItr().Оптимизация возвращаемого значения?

#include <iostream>
using namespace std;

struct ItrBase
{
    struct ItrImpl
    {
        virtual ~ItrImpl(){};
        virtual int vfunc() const = 0;
    };
    ItrBase(ItrImpl* peer) : m_peer(peer) { cout << "ItrBase constructor.\n"; };
    ~ItrBase() { cout << "ItrBase destructor. \n"; delete m_peer; };
    int getData() const { return m_peer->vfunc(); };

private:
    ItrImpl* const  m_peer;
};

class ArrayBase
{
    struct ItrImpl : public ItrBase::ItrImpl
    {
        virtual int vfunc() const { return 0; };
    };

public:
    ArrayBase() { cout << "ArrayBase constructor.\n"; };
    ~ArrayBase() { cout << "ArrayBase destructor.\n"; };
    virtual ItrBase createItr() { cout << "in AB's createItr()\n"; return ItrBase(new ItrImpl); };
};

class EnhancedArray : public ArrayBase
{
    struct ItrImpl : public ItrBase::ItrImpl
    {
        virtual int vfunc() const { return 1; };
    };

public:
    EnhancedArray() { cout << "EnhancedArray constructor.\n"; };
    ~EnhancedArray() { cout << "EnhancedArray destructor.\n"; };
    virtual ItrBase createItr() { cout << "in EA's createItr()\n"; return ItrBase(new ItrImpl); };
};

int main()
{
    ArrayBase ab;
    EnhancedArray ea;

    ItrBase itr = ab.createItr();  
    ItrBase eitr = ea.createItr();  //EnhancedItr assigned to ItrBase


    cout << "ArrayBase's Itr .vfunc(): " << itr.getData() <<std::endl;
    cout << "EnhancedArray's Itr .vfunc(): " << eitr.getData() <<std::endl;

    return 0;
}
0 голосов
/ 19 сентября 2010

совершенно другое, что вы можете сделать, это использовать,

BOOST_AUTO(iterator, array);

и пусть компилятор определит тип возвращаемого значения.

BOOST_AUTO

0 голосов
/ 19 сентября 2010

Вы столкнулись с проблемой под названием нарезка : createItr возвращает ссылку, а затем вы копируете ее в ItrBase по значению . Это как если бы вы сделали что-то вроде этого:

EnhancedItr itr1 = ...;
BaseItr itr2 = itr1;  // copy by-value
cout << itr2.vfunc();  // prints 1, not 0

Вы также теряете память: createItr возвращает недавно выделенный объект, но вы никогда не удаляете его. Это очень плохо, особенно если учесть, что итераторы массивов будут использоваться часто.

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