| 7.1 - Funções default |
|
Quando você cria uma classe, quatro funções default
são criadas automaticamente e serão utilizadas, a menos que você as sobrescreva. Essas
funções default são:
- O construtor default
- O construtor de cópia default
- O operador de atribuição default
- O destrutor default
O construtor default é invocado quando você declara uma
instância da classe sem passar qualquer parâmetro. Por exemplo, se você criar uma
classe Sample sem a definição explícita de um construtor, então o comando
seguinte invoca o construtor default para s:
Sample s;
A seguinte declaração com inicialização de s2 invoca
o construtor de cópia:
Sample s1;
Sample s2 = s1;
O destrutor default é chamado quando se encerra o escopo
dentro do qual a variável foi criada, e o construtor de atribuição é chamado quando
ocorre uma operação de atribuição normal.
Você pode sobrescrever qualquer um desses construtores,
definindo as suas próprias funções. Por exemplo, se você define explicitamente um
construtor para a classe, o construtor default não será criado pelo compilador.
O código seguinte vai nos ajudar a ter uma melhor
compreensão do que fazem o construtor e o destrutor default: |
|
#include <iostream.h>
class Class0
{
int data0;
public:
Class0 () { cout << "class0 constructor" << endl; }
~Class0 () { cout << "class0 destructor" << endl; }
};
class Class1
{
int data1;
public:
Class1 () { cout << "class1 constructor" << endl; }
~Class1 () { cout << "class1 destructor" << endl; }
};
class Class2: public Class1
{
int data2;
Class0 c0;
};
void main()
{
Class2 c;
} |
|
| A classe Clas2 não tem construtor nem destrutor
definidos explicitamente, mas esse código produz a seguinte saída: class1 constructor
class0 constructor
class0 destructor
class1 destructor
O que aconteceu é que o compilador criou automaticamente
tanto o construtor quanto o destrutor default para Clas2. O comportamento do
default construtor é chamar o construtor da classe base, bem como o construtor default
para cada um dos dados membro que são classes. O destrutor default chama o destrutor da
classe base e dos dados membro que são classes.
Digamos que você crie um novo construtor para Clas2 que
aceite um inteiro. O compilador ainda assim vai chamar os necessários construtores da
classe base e dos dados membro que são classes.
O código seguinte demonstra esse processo: |
|
class Class2: public
Class1
{
int data2;
Class0 c0;
public:
Class2(int i)
{
cout << "class2 constructor" << endl;
}
};
void main()
{
Class2 c(1);
} |
|
Isso também funciona e produz a seguinte saída:
class1 constructor
class0 constructor
class2 constructor
class0 destructor
class1 destructor
Mas agora você não pode mais declarar uma variável não inicializada do tipo Clas2 porque
não há mais um construtor default. O código seguinte demonstra: |
|
Class2 c(1); // OK
Class2 e; // not OK--no default constructor |
|
| É também possível declarar uma matriz de uma
classe, a menos que não haja um construtor default definido. Contudo, você pode
recriar o construtor default, criando explicitamente um construtor com uma lista de
parâmetros vazia, da mesma maneira que cria outros construtores para a classe.
O operador de atribuição e o construtor de cópia também são criados
automaticamente. Ambos apenas copiam os dados membro da instância à direita do sinal = para a
instância à esquerda. No caso de nossa classe Stack, nós queremos eliminar
essas funções default e usar funções próprias, para que a operação de atribuição
funcione corretamente. A seguir estão as duas novas funções para a classe Stack, e a
função Copy compartilhada por ambas: |
|
void Copy(const
Stack& s)
{
node *q=0;
node *p=s.top;
while (p)
{
if (top==0)
{
top = new node;
q=top;
}
else
{
q->next = new node;
q = q->next;
}
q->data = p->data;
p = p->next;
q->next=0;
}
}
Stack& operator= (const Stack& s) //assignment
{
if (this == & s)
return *this;
Clear();
Copy(s);
return *this;
}
Stack(const Stack& s): top(0) // copy constructor
{
Copy(s);
} |
|
| A função de atribuição se inicia
verificando o caso de auto-atribuição, como em s = s;
Se verifica tratar-se de auto-atribuição, a função não faz nada, ou seja, não
efetua a auto-atribuição. Não sendo auto-atribuição, a função limpa a instância
recipiente e copia a lista ligada existente na memória, de modo que a instância à
esquerda do operador de atribuição tenha sua própria cópia da pilha.
O construtor de cópia é basicamente o mesmo que qualquer
outro construtor. É usado para manejar os seguintes casos: |
|
Stack s1;
s1.Push(10);
s1.Push(20);
Stack s2(s1); // copy constructor invoked
Stack s3 = s1; // copy constructor invoked |
|
| Uma vez implementados o operador de atribuição e o
construtor de cópia, a classe
Stack está completa. Pode manejar qualquer condição
e funcionar corretamente. |
|
|
|