Pergunta

Estou extraindo arquivos de arquivos zip e rar em buffers brutos.Eu criei o seguinte para agrupar minizip e unrarlib:

Arquivo.hpp - Usado para acessar tudo.Se eu pudesse tornar todas as funções das outras classes inacessíveis de fora, eu o faria.(Na verdade, suponho que eu poderia ser amigo de todas as outras classes no Archive e usar retornos de chamada de funções privadas..., mas isso é muito indireto.)

#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_;
}

ArquivoBase.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 */
}

ArquivoDerivado.hpp "Derivado" sendo "Zip" ou "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_;
}

ArquivoDerivado.cpp

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

Alguém sugeriu que eu usasse esse design para poder fazer:

Archive archiveFile(pathToZipOrRar);
archiveFile.passThrough(extractParams); // yay polymorphism!
  • Como escrevo um cctor para Archive?

  • E quanto a op= para Arquivo?

  • O que posso fazer sobre "renomear" ArchiveBase::Data para Archive::Data?(Tanto o minizip quanto o unrarlib usam essas estruturas para entrada e saída.Os dados são genéricos para Zip & Rar e posteriormente são usados ​​para criar a respectiva estrutura da biblioteca.) Todo o resto é acessado via Archive, e eu gostaria de declarar Data em uma aula externa desta forma também.

Eu sei que poderia jogar fora minha corrente class Archive, nome ArchiveBase em Archive, e use uma função de fábrica global.No entanto, eu queria evitar o uso da função global.

Foi útil?

Solução

Primeiro de tudo, você não pode "copiar" uma classe abstrata porque não pode instanciar uma.Em vez disso, o que você deve fazer é configurar um std::tr1::shared_ptr dessa classe e passar um ponteiro.

Archive(ArchiveBase *_archiveBase)

Use uma função de fábrica fora da classe Archive para instanciação.

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");
  }

Para o operador =, simplesmente segurar um ponteiro inteligente como este e usar o "=" realizará a transferência segura de conhecimento entre as classes.Ele executa a contagem de referência e excluirá o ponteiro para que você não precise fazer isso e somente quando for seguro fazê-lo.

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

Deixe que os ponteiros inteligentes se preocupem em excluir, copiar e tudo mais para você.

Outras dicas

Trigos a sugestão funciona quando você pode pagar cópias superficiais e um relacionamento N-1.Ele falha quando as subclasses ArchiveBase contêm dados individuais específicos para cada instância do Archive e não são compartilhados entre vários objetos normalmente.

Uma abordagem alternativa para uma função createArchive() global é adicionar um método clone virtual abstrato() ao ArchiveBase e, em seguida, defini-lo em cada subclasse (ArchiveZip,ArchiveRar) para replicar adequadamente uma cópia superficial ou profunda conforme necessário.

Você pode então chamar archive_.clone() do construtor de cópia do Archive ou operator= se archive_ não for NULL.(Certifique-se de excluí-lo (gratuitamente) depois!)

O que posso fazer para "renomear" ArchiveBase::Data para Archive::Data?(Tanto o minizip quanto o unrarlib usam essas estruturas para entrada e saída.Os dados são genéricos para Zip e Rar e posteriormente são usados ​​para criar a respectiva estrutura da biblioteca.)

Existem várias opções:

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

Ou você pode manter uma referência retroativa no ArchiveBase para o objeto Archive correspondente, que pode então ser consultado em busca de dados.

Embora tenha cuidado!É fácil dessincronizar essas informações duplicadas.(E você pode entrar em loops de arquivo de cabeçalho.) É por isso que sou a favor de passar o esse ponteiro ao redor!Pode sempre pré-declarar "class Archive;" e depois usar os ponteiros Archive* sem incluir Archive.hpp no ficheiro de cabeçalho.(Embora você ainda precise incluir Archive.hpp no ​​arquivo .cpp.)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top