Как написать cctor и op = для фабричного класса с ptr для абстрактного поля члена? - PullRequest
2 голосов
/ 29 марта 2010

Я извлекаю файлы из zip и rar архивов в необработанные буферы. Я создал следующее, чтобы обернуть minizip и unrarlib:

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

#include "ArchiveBase.hpp"
#include "ArchiveDerived.hpp"
class Archive {
  public:
    Archive(string path) {
      /* logic here to determine type */
      switch(type) {
        case RAR:
          archive_ = new ArchiveRar(path);
          break;
        case ZIP:
          archive_ = new ArchiveZip(path);
          break;
        case UNKNOWN_ARCHIVE:
          throw;
          break;
      }
    }
    Archive(Archive& other) {
      archive_ = // how do I copy an abstract class?
    }
    ~Archive() { delete archive_; }
    void passThrough(ArchiveBase::Data& data) { archive_->passThrough(data); }
    Archive& operator = (Archive& other) {
      if (this == &other) return *this;
      ArchiveBase* newArchive = // can't instantiate....
      delete archive_;
      archive_ = newArchive;
      return *this;
    }
  private:
    ArchiveBase* archive_;
}

ArchiveBase.hpp

class ArchiveBase {
  public:
    // Is there any way to put this struct in Archive instead,
    //  so that outside classes instantiating one could use
    //  Archive::Data instead of ArchiveBase::Data?
    struct Data {
      int field;
    };
    virtual void passThrough(Data& data) = 0;
    /* more methods */
}

ArchiveDerived.hpp"Производным" является "Zip" или "Rar"

#include "ArchiveBase.hpp"
class ArchiveDerived : public ArchiveBase {
  public:
    ArchiveDerived(string path);
    void passThrough(ArchiveBase::Data& data);
  private:
    /* fields needed by minizip/unrarlib */
    // example zip:
    unzFile zipFile_;
    // example rar:
    RARHANDLE rarFile_;
}

ArchiveDerived.cpp

#include "ArchiveDerived.hpp"
ArchiveDerived::ArchiveDerived(string path) { //implement }
ArchiveDerived::passThrough(ArchiveBase::Data& data) { //implement }

Кто-то предложил мне использовать этот дизайн, чтобы я мог сделать:

Archive archiveFile(pathToZipOrRar);
archiveFile.passThrough(extractParams); // yay polymorphism!
  • Как написать редактор для Archive?

  • А как насчет op = для архива?

  • Что я могу сделать с "переименованием" ArchiveBase::Data в Archive::Data? (И minizip, и unrarlib используют такие структуры для ввода и вывода. Данные являются общими для Zip & Rar, а затем используются для создания структуры соответствующей библиотеки.) Ко всему остальному обращаются через Archive, и я хотел бы сделать объявление Data и во внешнем классе тоже.

Я знаю, что могу выбросить свой текущий class Archive с именем ArchiveBase в Archive и использовать глобальную фабричную функцию. Однако я хотел избежать использования глобальной функции.

Ответы [ 2 ]

3 голосов
/ 29 марта 2010

Прежде всего, вы не можете "скопировать" абстрактный класс, потому что вы не можете создать его экземпляр. Вместо этого вам нужно настроить std :: tr1 :: shared_ptr этого класса и передать указатель.

Archive(ArchiveBase *_archiveBase)

Использовать фабричную функцию вне класса Archive для создания экземпляров.

Archive createArchive(string _path, int _type){
    switch(type) {
    case RAR:
      return Archive( new ArchiveRar(path) );
    case ZIP:
      return Archive( new ArchiveZip(path) );
    case UNKNOWN_ARCHIVE:
      throw exception("Unknown archive format");
      break;
    default:
      throw exception("Improper archive type");
  }

Для оператора = простое удерживание умного указателя, такого как этот, и использование «=» обеспечит безопасную передачу знаний между классами. Он выполняет подсчет ссылок и удаляет указатель, поэтому вам не нужно делать это, и только тогда, когда это безопасно.

Archive& operator = (Archive& other) {
  m_ArchiveBasePtr = other.m_ArchiveBasePtr;
  return *this;
}

Пусть умные указатели беспокоятся об удалении, копировании и всем этом за вас.

2 голосов
/ 29 марта 2010

Пшеница предложение работает, когда вы можете позволить себе мелкие копии и отношения N-1. Он ломается, когда подклассы ArchiveBase содержат конкретные данные 1-на-1 для каждого экземпляра архива, и не грациозно распределяется между несколькими объектами.

Альтернативный подход к глобальной функции createArchive () состоит в том, чтобы добавить абстрактный виртуальный метод clone () в ArchiveBase, а затем определить его в каждом подклассе (ArchiveZip, ArchiveRar) для соответствующей репликации мелкой или глубокой копии при необходимости.

Затем вы можете вызвать archive_.clone () из конструктора или оператора копирования архива, если archive_ не равен NULL. (Обязательно удалите (бесплатно) впоследствии!)

Что я могу сделать с "переименованием" ArchiveBase :: Data в Archive :: Data? (И minizip, и unrarlib используют такие структуры для ввода и вывода. Данные являются общими для Zip & Rar, а затем используются для создания структуры соответствующей библиотеки.)

Есть несколько вариантов:

Archive::passThrough() { archive_ -> passThrough( this->getData() ) }
Archive::passThrough() { archive_ -> passThrough( this ) }

Или вы можете поддерживать обратную ссылку в ArchiveBase на соответствующий объект Archive, который затем может быть запрошен для данных.

Хотя будьте осторожны! Такую дублированную информацию легко вывести из синхронизации. (И вы можете попасть в циклы заголовочных файлов.) Поэтому я предпочитаю передавать указатель this вокруг! Вы всегда можете объявить "Архив класса"; и затем используйте указатели Archive *, не включая Archive.hpp в заголовочный файл. (Хотя вам все равно нужно будет включить Archive.hpp в файл .cpp.)

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