Pergunta

Em C++, que alternativas tenho para expor uma coleção, a partir do ponto de vista de desempenho e integridade de dados?

O meu problema é que eu queira retornar uma lista interna de dados para o chamador, mas eu não quero gerar uma cópia.Thant me deixa com retornando a referência a uma lista, ou um ponteiro para a lista.No entanto, eu não sou louco de deixar o chamador de alterar os dados, eu só quero que ele leia os dados.

  • Tenho que escolher entre o desempenho e integridade de dados?
  • Se assim, é, em geral, melhor ir de um jeito ou é específico para o caso?
  • Existem outras alternativas?
Foi útil?

Solução

RichQ resposta é uma técnica razoável, se você estiver usando uma matriz, vetor, etc.

Se você estiver usando uma coleção que não está indexada pelos valores ordinais...ou pensar em você pode precisar em algum ponto no futuro próximo...em seguida, você pode querer considerar a expor o seu próprio tipo de iteração(s), e associados begin()/end() métodos:

class Blah
{
public:
   typedef std::vector<mydata> mydata_collection;
   typedef myDataCollection::const_iterator mydata_const_iterator;

   // ...

   mydata_const_iterator data_begin() const 
      { return myPreciousData.begin(); }
   mydata_const_iterator data_end() const 
      { return myPreciousData.end(); }

private:
   mydata_collection  myPreciousData;
};

...que você pode usar no modo normal:

Blah blah;
for (Blah::mydata_const_iterator itr = blah.data_begin();
   itr != blah.data_end();
   ++itr)
{
   // ...
}

Outras dicas

Muitas vezes o chamador deseja ter acesso apenas para iterar sobre a coleção.Ter uma página fora do Ruby livro e fazer a iteração de um particular aspecto de sua classe.

#include <algorithm>
#include <boost/function.hpp>

class Blah
{
  public:
     void for_each_data(const std::function<void(const mydata&)>& f) const
     {
         std::for_each(myPreciousData.begin(), myPreciousData.end(), f);
     }

  private:
     typedef std::vector<mydata> mydata_collection;
     mydata_collection  myPreciousData;
};

Com essa abordagem, você não está expondo nada sobre seus internos, por exemplo,que você mesmo tem uma coleção.

Talvez algo como isto?

const std::vector<mydata>& getData()
{
  return _myPrivateData;
}

A vantagem aqui é que é muito, muito simples, e tão seguro como você getin C++.Você pode conjurar este, como RobQ sugere, mas não há nada que você pode fazer o que impediria alguém de que se você não está copiando.Aqui, você teria que usar const_cast, o que é muito fácil de detectar se você está olhando para ele.

Os iteradores, como alternativa, você pode obter praticamente a mesma coisa, mas é mais complicado.A única vantagem de utilizar iteradores aqui (que eu posso pensar) é que você pode ter um melhor encapsulamento.

Uso constante de referência ou compartilhado ponteiro só vai ajudar se o conteúdo da coleção subjacente não mudam ao longo do tempo.

Considere o seu design.O chamador realmente precisa ver a matriz interna?Você pode reestruturar o código para que o objeto chamador diz o que fazer com a matriz?E. g., se o chamador pretende pesquisar a matriz, poderia o proprietário do objeto fazê-lo?

Você poderia passar uma referência ao resultado vetor para a função.Em alguns compiladores, o que pode resultar em pouco mais rápido código.

Eu recomendaria tentar redesenhar primeiro, indo com uma solução limpa segunda, otimizando o desempenho de terceiro (se necessário).

Uma vantagem de @Shog9 e @RichQ soluções é que eles de-casal o cliente a partir da implementação de colecção.

Se você decidir th alterar o seu tipo de coleção para outra coisa, os clientes continuarão a funcionar.

O que você quer é somente leitura o acesso sem copiar todo o blob de dados.Você tem algumas opções.

Em primeiro lugar, você pode simplesmente retornar uma constante refererence para qualquer que seja o seu recipiente de dados é, como sugerido acima:

const std::vector<T>& getData() { return mData; }

Isto tem a desvantagem de concretude:você não pode mudar a forma de armazenar os dados internamente, sem alterar a interface da sua classe.

Em segundo lugar, você pode retornar const-ed ponteiros para os dados reais:

const T* getDataAt(size_t index)
{
   return &mData[index];
}

Este é um pouco mais agradável, mas também exige que você forneça um getNumItems chamada, e proteger contra o out-of-bounds índices.Também, const-dade de seus ponteiros é facilmente lançar fora, e que os dados agora ler-escrever.

Outra opção é oferecer um par de iteradores, que é um pouco mais complexo.Este tem as mesmas vantagens de ponteiros, bem como, não (necessariamente) que fornecem um getNumItems chamada, e há muito mais trabalho envolvido para remover os iteradores de seus const-ness.

Provavelmente a maneira mais fácil de gerenciar isso é usando um Impulso Gama:

typedef vector<T>::const_iterator range_iterator_type;
boost::iterator_range< range_iterator_type >& getDataRange()
{
    return boost::iterator_range(mData.begin(), mData.end());
}

Este tem as vantagens de intervalos sendo compostas, que podem ser filtradas, etc, como você pode ver na site.

Usando const é uma escolha razoável.Você também pode querer verificar para fora o impulso da biblioteca C++ para a sua compartilhado execução do ponteiro.Ele oferece as vantagens de ponteiros i.e.você pode ter o requisito para devolver uma compartilhado ponteiro para "null" que uma referência não iria permitir.

http://www.boost.org/doc/libs/1_36_0/libs/smart_ptr/smart_ptr.htm

No seu caso, você gostaria de fazer compartilhado ponteiro do tipo const proibir escreve.

Se você tem um std::list do velho e simples de dados (o que .NET seria chamada de 'tipos de valor'), retornando uma constante de referência para essa lista vai ficar bem (ignorando as coisas más, como const_cast)

Se você tem um std::list de apontadores (ou boost::shared_ptr's), em seguida, que só irá parar você modificando a coleção, e não os itens no a coleção.Meu C++ é muito enferrujado para ser capaz de dizer a resposta para o que neste ponto :-(

Sugiro o uso de chamadas de retorno ao longo das linhas de EnumChildWindows.Você vai ter de encontrar meios para impedir que o usuário altere seus dados.Talvez usar um const ponteiro de referência.

Por outro lado, você poderia passar uma cópia de cada elemento para a função de retorno de chamada substituindo a cópia a cada vez.(Você não quer gerar uma cópia de toda a sua coleção.Estou apenas sugerindo a realização de uma cópia de um elemento de cada vez.Isso não deve demorar muito tempo/memória).

MyClass tmp;
for(int i = 0; i < n; i++){
    tmp = elements[i];
    callback(tmp);
}

Os seguintes dois artigos elaborados em algumas das questões envolvidas e a necessidade de, encapsulamento de classes do recipiente.Apesar de não fornecer uma completa trabalhou solução, essencialmente, levar para a abordagem dada por Shog9.

Parte 1: Encapsulamento e Vampiros
Parte 2 (inscrição gratuita: não é necessário para ler este artigo): Acidente De Trem Manchas
por Kevlin Henney

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