Dando ao acesso da criança ao membro do pai por referência - é OK?
-
20-08-2019 - |
Pergunta
C ++ novato pergunta. Por favor, verifique que eu estou fazendo certo.
Eu tenho uma classe de aplicações globais desova São pequenas as crianças e eu preciso dar acesso as crianças para algumas das instalações de aplicativos. Então eu decidi passá-las para as crianças por referência.
Eu testei a idéia, como mostrado abaixo. Parece funcionar bem. Eu só queria ter certeza que eu não estou fazendo algo perigoso. Poderia estar lá qualquer armadilhas I negligenciados?
Dad cria filhos e dá-lhes as chaves do carro:
#include <iostream>
using namespace std;
class CCarKeys
{
public:
CCarKeys(const string& Name) : _Name(Name) {}
string _Name;
};
class CChild
{
public:
CChild(CCarKeys& CarKeys) : _Name("Child"), _CarKeys(CarKeys) {}
string _Name;
CCarKeys& _CarKeys;
void TestHasKeys() {cout << "I got " << _CarKeys._Name << endl;}
};
class CDad
{
public:
CDad() : _Name("Dad"), _HondaCarKeys("Honda keys"), _ChevyCarKeys("Chevy keys") {}
string _Name;
CCarKeys _HondaCarKeys;
CCarKeys _ChevyCarKeys;
CChild *_Boy;
CChild *_Girl;
void MakeBoy() {_Boy= new CChild(_HondaCarKeys);}
void MakeGirl() {_Girl= new CChild(_ChevyCarKeys);}
};
int main ()
{
CDad Dad;
Dad.MakeBoy();
Dad.MakeGirl();
Dad._Boy->TestHasKeys();
Dad._Girl->TestHasKeys();
}
Solução
Parece bom para mim (se as chaves é tudo que eles precisam). Eles podem precisar de alguns outros serviços de papai que são solicitados mais tarde - como:
Wallet += MyDad.GasMoney(REQUEST_MAX_AND_PROMISE_TO_BE_HOME_BY_10PM) ;
Mas eles não têm uma referência ao pai, para que eles não serão capazes de fazer isso. Então eu teria o construtor CChild tomar uma referência this
também.
class ICashProvider {
public:
virtual money Request(IPerson,CashRequestFlags) ;
};
class IChaffeur {
public:
virtual void Drive(IPerson[]) ;
};
etc.
E então construtor CChild
precisa tomar ICashProvider
e IChaffeur
, como seria CWife
e CGirlfriend
(e CBoyfriend
, talvez). Neste ponto, eu acho que você pode perceber que este nível de granularidade é inútil em face das responsabilidades de Dad
e você apenas dar a todos this
e tem pedidos Dad
autenticar forçando chamadores de enviar sua própria this
em alguns métodos, para que você não tem Dad
realizando incesto ou mudar a fralda do CWife
.
Outras dicas
passagem por referência é o mesmo que passar pelo ponteiro, exceto para a semântica eo fato de que você não pode fazer nada com o próprio ponteiro se passando por referência.
Seu código é OK.
No seu caso particular, as chaves do carro não são susceptíveis de ser concedida de forma permanente, mas sim solicitado quando necessário e concedido na base por solicitação. Por isso é mais
class Dad
{
/** may return NULL if no keys granted */
CarKeys *requestKeys(CChild forChild);
}
No caso mais genral de classe aplicativo principal e as crianças, que tal criar os dados objeto compartilhado por aplicativo e crianças em main (), e passando thereferences a todos.
É possível e em seu código não está causando danos. Mas é perigoso, porque se você copiar CDAD, em seguida, as chaves e ponteiros serão copiados junto. Os objectos que os ponteiros irá apontar para, e a referência dentro desses objectos, no entanto, irá permanecer o mesmo. Se o objeto cdad original, então sai do escopo, as referências nos objetos referenciados pelos ponteiros estão pendurados, fazendo referência a nenhum objeto mais válido.
Talvez você pode vidas reversa: Criar as chaves na pilha, e as crianças como membros normais dentro da classe. Então, se você copiar o pai, as crianças são copiados, mas as chaves não são. Eu acho que as chaves são imutáveis, assim você pode compartilhar a mesma chave entre vários filhos.
Este leva a outro ponto: se as chaves são razoáveis ??pequeno (leia-se: não enorme) e imutável (assim você não tem anomalias de atualização se você mudar uma chave, mas não os outros), considere a criação it não na pilha também - então eles são automaticamente copiados junto também, e passá-las para as crianças quando eles precisam da chave. Você pode torná-los ponteiros normais das crianças, mas eu acho que é feio, porque a criança não faz conter uma chave - mas usa-lo. Assim, um ponteiro / referência ou um parâmetro de função se encaixa bem, mas não um "real" membro de dados.
Se você está indo com a chave compartilhada e as chaves-on-heap, você deve usar ponteiros inteligentes - porque você tem que manter o controle de todas as crianças e pais. Se a última criança / pai sai do escopo, você tem que excluir a chave novamente. Você usa boost::shared_ptr
para isso:
class CCarKeys
{
public:
CCarKeys(const string& Name) : _Name(Name) {}
string _Name;
};
class CChild
{
public:
CChild(boost::shared_ptr<CCarKeys> const& CarKeys)
: _Name("Child"), _CarKeys(CarKeys) {}
string _Name;
boost::shared_ptr<CCarKeys> _CarKeys;
void TestHasKeys() {cout << "I got " << _CarKeys._Name << endl;}
};
class CDad
{
public:
// NOTE: null the kid pointers *if* you use raw pointers, so you can check whether
// a boy or girl is present. Without nulling them explicitly, they have
// indeterminate values. Beware. shared_ptr's however will automatically
// initialized to default "null" values
CDad() :
_Name("Dad"),
_HondaCarKeys(new CCarKeys("Honda keys")),
_ChevyCarKeys(new CCarKeys("Chevy keys")) {}
string _Name;
boost::shared_ptr<CCarKeys> _HondaCarKeys;
boost::shared_ptr<CCarKeys> _ChevyCarKeys;
// also use shared_ptr for the kids. try to avoid raw pointers.
boost::shared_ptr<CChild> _Boy;
boost::shared_otr<CChild> _Girl;
void MakeBoy() {_Boy.reset(new CChild(_HondaCarKeys));}
void MakeGirl() {_Girl.reset(new CChild(_ChevyCarKeys));}
};
// main can be used unchanged
É claro, você pode evitar toda esta complexidade, apenas fazendo a classe não-copiável CDAD. Então você pode usar a sua solução original, apenas com fazer as crianças usam um shared_ptr e fazer as crianças não-copiável também. Idealmente, deve-se usar um ponteiro não-compartilhado, como auto_ptr
, mas que auto_ptr tem algumas armadilhas também, que shared_ptr todas evita:
class CCarKeys
{
public:
CCarKeys(const string& Name) : _Name(Name) {}
string _Name;
};
class CChild
{
public:
CChild (CCarKeys& CarKeys)
: _Name("Child"), _CarKeys(CarKeys) {}
string _Name;
CCarKeys &_CarKeys;
void TestHasKeys() {cout << "I got " << _CarKeys._Name << endl;}
private:
CChild(CChild const&); // non-copyable
CChild & operator=(CChild const&); // non-assignable
};
class CDad
{
public:
CDad() :
_Name("Dad"),
_HondaCarKeys("Honda keys"),
_ChevyCarKeys("Chevy keys") {}
string _Name;
CCarKeys _HondaCarKeys;
CCarKeys _ChevyCarKeys;
// also use shared_ptr for the kids. try to avoid raw pointers.
boost::shared_ptr<CChild> _Boy;
boost::shared_otr<CChild> _Girl;
void MakeBoy() {_Boy.reset(new CChild(_HondaCarKeys));}
void MakeGirl() {_Girl.reset(new CChild(_ChevyCarKeys));}
private:
CDad(CDad const&); // non-copyable
CDad & operator=(CDad const&); // non-assignable
};
Se eu tivesse que implementar uma hierarquia de classes, eu iria com essa solução, ou apenas deixar as chaves como membros, e passar / criá-los quando necessário para as crianças. Algumas outras notas sobre o código:
- Melhor deixar cair o "_" de membros ou colocá-los no final ou usar alguma outra notação. Um nome que começa com um sublinhado e é seguido por uma letra maiúscula é reservado pelo C ++ implementações (compilador, C ++ std lib ...).
- Eu, pessoalmente encontrá-lo confuso para ter nomes de membros e variáveis ??começar com uma letra maiúscula. Eu vi que só muito raramente. Mas isso não é muito de se preocupar, é apenas sobre o estilo pessoal.
- Existe uma regra famosa ( Zero-One-Infinito) que os estados quando você tem duas coisas de alguma coisa, você geralmente deve ser capaz de ter arbitrárias muitas coisas de que. Então, se você pode ter duas crianças - por que não têm muitos deles? Dois parece uma escolha arbitrária. Mas pode ter uma boa razão no seu caso -. Modo ignorar isso quando, no seu caso, faz sentido