Pergunta

eu tenho uma aula CContainer que tem alguns membros CMemberX, CMemberY, que são independentes um do outro e de outros CClientA, CClientB aulas que usam CContainer.

#include "MemberX.h"
#include "MemberY.h"

class CContainer
{
public:
    CMemberX & GetX() const { return m_x; }
    CMemberY & GetY() const { return m_y; }

private:
    CMemberX m_x;
    CMemberY m_y;
};

Quero evitar ter que recompilar todos CClient classes ao modificar uma das CMember classes usando declarações futuras e alocação dinâmica de m_x e m_y.

Inicialmente, fiz dicas aos membros:

// Container.h
class CMemberX;
class CMemberY;

class CContainer
{
public:
    CContainer();
    ~CContainer();

    CMemberX & GetX() const { ASSERT(m_pX != NULL); return *m_pX; }
    CMemberY & GetY() const { ASSERT(m_pY != NULL); return *m_pY; }

private:
    CMemberX* m_pX;
    CMemberY* m_pY;
};

// Container.cpp
#include "Container.h"
#include "MemberX.h"
#include "MemberY.h"

// Allocate members on heap
CContainer::CContainer() : m_pX(new CMemberX()), m_pY(new CMemberY()) {}
CContainer::~CContainer() { delete m_pX; delete m_pY; }

Então pensei que poderia também usar referências em vez de ponteiros, para que ficasse mais parecido com o código original:

// Container.h
class CMemberX;
class CMemberY;

class CContainer
{
public:
    CContainer();
    ~CContainer();

    CMemberX & GetX() const { return m_x; }
    CMemberY & GetY() const { return m_y; }

private:
    CMemberX & m_x;
    CMemberY & m_y;
};

// Container.cpp
#include "Container.h"
#include "MemberX.h"
#include "MemberY.h"

// Allocate members on heap
CContainer::CContainer() : m_x(*new CMemberX()), m_y(*new CMemberY()) {}
CContainer::~CContainer() { delete &m_x; delete &m_y; }

O que eu não gosto nos membros ponteiro é que visual como os ponteiros poderiam ser NULL ou os objetos serão substituídos em tempo de execução, o que não é o caso.

O que não gosto nas referências é que o código no CTor e no DTor parece um pouco hackeado.

Qual abordagem é preferível?Existe uma solução melhor?

Nota sobre cópia/atribuição: Instâncias do CContainer classe não será, em nenhuma circunstância, copiada ou atribuída uma à outra.

Foi útil?

Solução

Acho que é para isso que servem as variáveis ​​​​const:

CMember * const m_x;

Não é possível alterar m_x após a inicialização...

Outras dicas

Eu acho que é um pouco surpreendente usar uma referência quando há semântica de propriedade. Não necessariamente Faça uma má idéia, considerando tudo, mas isso pesa.

Acho que só usei referências como membros nos casos em que ambos:

  • Um objeto é fornecido ao construtor, necessário para sobreviver a esse objeto.
  • A tarefa é proibida de qualquer maneira.

Por exemplo, dependências injetadas, como objetos de fábrica ou serviço, podem ser adequadas. Com isso, no C ++, você prefere injetar dependências com parâmetros de modelo em vez de objetos, para que o problema não surja.

Também acho que quanto mais eu uso C ++, mais quero que os tipos sejam atribuíveis, a menos que haja um bom motivo para não ser. O truque usual para reduzir as dependências do tempo de compilação da maneira que você deseja é "Pimpl", não "RIMPL", por um motivo. Ao mudar de um membro do objeto para um membro de referência, você está tornando sua classe não copiável, onde anteriormente talvez fosse copável. Esse detalhe da implementação não deve restringir a interface da classe. Com o PIMPL, você pode implementar de maneira limpa atribuição e troca. Com essas referências, você teria que atribuir ou trocar os dois membros. Se a segunda troca falhar, você perdeu a forte garantia de exceção: embora se suas classes CMemberx e CMembery tiverem atribuição e troca sem falhas, isso não importa.

Então, acho que não gosto da referência neste caso, mas não vi o resto do seu código. Talvez haja alguma razão pela qual nenhuma das preocupações sobre a atribuição se aplique - por exemplo, se o CContainer é uma classe RAII, geralmente as únicas operações do ciclo de vida que deve apoiar são a construção e a destruição.

Houve muitas perguntas aqui sobre a conveniência de usar referências como membros (por exemplo Devo preferir ponteiros ou referências nos dados de membros?), e parece -me que a opinião da maioria (que também é minha) é - não. Se você não deseja que os ponteiros sejam alterados, faça -os const - não consigo ver como, dado o seu código, eles podem ser nulos.

Uma rápida maneira ultra baixa tecnologia que costumava verificar que o balanceamento de carga está funcionando como esperado é comparar a atividade nos logs do IIS entre os vários servidores para o aplicativo Web do SharePoint.No seu caso, parece que você está fazendo mais falhas que o balanceamento de carga, o que significa que a maioria (se nem todos) do tráfego da Web deve ser para o WFE, então o arquivo de log do IIS deve ser substancial e ativo, enquanto o IIS registrouO servidor de aplicativos deve estar ocioso ou quase ocioso.

Há coisas que podem inclinar o log do iIS do App Server, como um rastreador, mas aqueles geralmente têm uma string useragente distinta e podem ser excluídos.

Eu ficaria curioso se alguém tiver uma maneira mais precisa de confirmá-lo, no entanto.

Steve Jessop já mencionou o idioma do PIMPL de passagem, mas acho que você deve verificar isso se ainda não o encontrou: Firewalls de compilação

O segundo bloco de código na sua pergunta, você tem de membro privado ponteiros que são inicializadas e destruída, juntamente com a classe pai.Esta informação deve ser suficiente para o leitor de seu código para perceber o que está acontecendo.

Além disso, você pode declarar os ponteiros const: CMember* const m_pX; para indicar que eles não podem ser alterados depois da inicialização.Agora o compilador vai pegar mudanças acidentais.

Você não está realmente comprando nada.
(Tempo de compilação um pouco mais curto em situações limitadas).

Mas você está jogando muito outro código que precisa ser mantido no seu prato.

Se esses objetos são membros naturais, deixe -os como membros.
Ao criá -los na pilha e armazená -los como indicadores ou referências, você deve fazer um monte de perguntas pegajosas que precisam de código para respondê -las.

  • O que acontece quando você copia construir o objeto.
    • Eu normalmente esperava que o obejct e todos os seus membros fossem copiados.
      Usando ponteiros ou referências, você precisará fazer um trabalho extra para replicar essa funcionalidade que já está fornecida.
  • O que acontece quando você atribui os objetos.
    • As referências não funcionarão (embora você contorne isso usando referências de impulso).
      Mas você ainda tem o problema de esperar que os membros sejam copiados.
  • O que acontece quando você exclui o objeto.
    • Se você implementou tudo corretamente para deixar cópias bem.
      Caso contrário, você precisa começar a pensar em ponteiros compartilhados para implementar a funcionalidade necessária.

Como está as versões 2 e 3 (do código na questão) são seriamente falhos e a única versão que realmente funciona é 1.

Na minha opinião, o simples fato de que os custos de manutenção serão muito mais baixos com a versão 1, que recomendar uma das versões 2 ou 3 é contra -produtiva. O tempo extra para compilar mais uma classe quando um membro é alterado é relativamente pequeno em comparação com a complexidade que você está adicionando ao código.

Além disso, você menciona no comentário de outra pessoa que o código não é tão limpo quanto o descrito acima. Isso apenas enfatiza meu argumento de que essa é uma otimização ruim que dificulta a funcionamento da classe corretamente e mantê -la mantida nesse estado.

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