C ++ construtor de cópia implícita para uma classe que contém outros objetos
-
05-07-2019 - |
Pergunta
Eu sei que o compilador, por vezes, fornece um construtor de cópia padrão, se você não implementar-se. Estou confuso sobre o que exatamente esse construtor faz. Se eu tiver uma classe que contém outros objetos, nenhuma das quais tem um construtor de cópia declarou, qual será o comportamento ser? Por exemplo, uma classe como este:
class Foo {
Bar bar;
};
class Bar {
int i;
Baz baz;
};
class Baz {
int j;
};
Agora, se eu fizer isso:
Foo f1;
Foo f2(f1);
Qual será o construtor de cópia padrão fazer? Será que o construtor de cópia gerado pelo compilador em Foo
chamar o construtor gerado pelo compilador em Bar
para copiar bar
, que irá chamar o construtor de cópia gerado pelo compilador em Baz
?
Solução
Foo f1;
Foo f2(f1);
Sim, isso vai fazer o que você espera que ele:
copiar os f2 construtor Foo :: Foo (Foo const &) é chamado.
Esta cópia constrói sua classe base e, em seguida, cada membro (de forma recursiva)
Se você definir uma classe como esta:
class X: public Y
{
private:
int m_a;
char* m_b;
Z m_c;
};
Os seguintes métodos será definido pelo seu compilador.
- Construtor (default) (2 versões)
- Construtor (cópia)
- Destructor (padrão)
- Operador de atribuição
Construtor: Padrão:
Na verdade, existem dois construtores padrão.
Um é usado para zero-initialization
enquanto o outro é usado para value-initialization
. O usada depende se você usar ()
durante a inicialização ou não.
// Zero-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Zero-Initialization version'
,m_a(0) // Default construction of basic PODS zeros them
,m_b(0) //
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Zero-Initialization version'
{
}
// Value-Initialization compiler generated constructor
X::X()
:Y() // Calls the base constructor
// If this is compiler generated use
// the `Value-Initialization version'
//,m_a() // Default construction of basic PODS does nothing
//,m_b() // The values are un-initialized.
m_c() // Calls the default constructor of Z
// If this is compiler generated use
// the `Value-Initialization version'
{
}
Notas: Se a classe base ou quaisquer membros não tem um construtor padrão visível válido, então o construtor padrão não pode ser gerado. Este não é um erro a menos que suas tentativas de código para usar o construtor padrão (então somente um erro de tempo de compilação).
Construtor (cópia)
X::X(X const& copy)
:Y(copy) // Calls the base copy constructor
,m_a(copy.m_a) // Calls each members copy constructor
,m_b(copy.m_b)
,m_c(copy.m_c)
{}
Notas: Se a classe base ou quaisquer membros não têm um construtor de cópia visível válido, então o construtor de cópia não pode ser gerado. Este não é um erro a menos que suas tentativas de código para usar o construtor de cópia (então somente um erro de tempo de compilação).
Atribuição Operador
X& operator=(X const& copy)
{
Y::operator=(copy); // Calls the base assignment operator
m_a = copy.m_a; // Calls each members assignment operator
m_b = copy.m_b;
m_c = copy.m_c;
return *this;
}
Notas: Se a classe base ou quaisquer membros não têm um operador de atribuição viável válido, então o operador de atribuição não pode ser gerado. Este não é um erro a menos que suas tentativas de código para usar o operador de atribuição (então somente um erro de tempo de compilação).
Destructor
X::~X()
{
// First runs the destructor code
}
// This is psudo code.
// But the equiv of this code happens in every destructor
m_c.~Z(); // Calls the destructor for each member
// m_b // PODs and pointers destructors do nothing
// m_a
~Y(); // Call the base class destructor
- Se qualquer construtor (incluindo cópia) é declarado, em seguida, o construtor padrão não é implementada pelo compilador.
- Se o construtor de cópia é declarado, em seguida, o compilador não irá gerar um.
- Se o operador de atribuição é declarada, em seguida, o compilador não irá gerar um.
- Se um destruidor é declarado o compilador não irá gerar um.
Olhando para o código seguintes construtores de cópia são gerados:
Foo::Foo(Foo const& copy)
:bar(copy.bar)
{}
Bar::Bar(Bar const& copy)
:i(copy.i)
,baz(copy.baz)
{}
Baz::Baz(Baz const& copy)
:j(copy.j)
{}
Outras dicas
O compilador fornece um construtor de cópia a menos que você declarar (nota: não definir ) um você mesmo. O construtor de cópia gerado pelo compilador simplesmente chama o construtor de cópia de cada membro da classe (e de cada classe base).
O mesmo é verdade para o operador de atribuição e o destruidor, BTW. É diferente para o construtor padrão, porém: que é fornecido pelo compilador somente se você não declarar qualquer outro construtor si mesmo.
Sim, a cópia do construtor executa gerados pelo compilador uma cópia membro-wise, na ordem em que os membros são declarados na classe que contém. Se qualquer um dos tipos de membro não se oferecem um construtor de cópia, o candidato a cópia construtor da classe que contém não pode ser gerado. Pode ainda ser possível escrever um manualmente, se você pode decidir sobre alguns meios adequados para inicializar o valor do membro que não pode ser construído-copy -. Talvez usando um de seus outros construtores
A C ++ construtor de cópia padrão cria um rasa copiar. Uma cópia superficial não irá criar novas cópias de objetos que o objeto original pode referência; os antigos e os novos objetos simplesmente contêm ponteiros distintas para o mesmo local de memória.
O compilador irá gerar os construtores necessários para você.
No entanto, assim que você definir uma cópia construtor mesmo, o compilador desiste gerando qualquer coisa para essa classe e vai dar e de erro se você não tem os construtores adequados definidos.
Usando o seu exemplo:
class Baz {
Baz(const Baz& b) {}
int j;
};
class Bar {
int i;
Baz baz;
};
class Foo {
Bar bar;
};
Não Tentando padrão instanciar ou copiar-construção Foo irá lançar um erro desde Baz é copiar-construível eo compilador não pode gerar o padrão e copiar constroctor para Foo.