C/C++ · 11 min read · Oct 10, 2025
Aprendendo C/C++ Passo a Passo - Página 16
16. Passo a Passo C/C++ — Programação C++ - Polimorfismo
Polimorfismo
| | 1. Sobrecarga de Função
- Polimorfismo
- Tipos de polimorfismo
- Funções membro normais acessadas com ponteiros
- Função Virtual
- Função Pura
- Atribuição e Inicialização por Cópia
- O Construtor de CÓPIA
- Ponteiro ‘this’ |
1. Sobrecarga de Função
Se uma função com seu nome diferir pelo comportamento dos argumentos, é chamada de polimorfismo de funções ou sobrecarga de função.
| | // Um programa de exemplo para demonstrar o uso da sobrecarga de função
#include
using namespace std;
void printline()
{
for(int i=0;i<=80; i++) cout << “-“;
}
void printline(int n)
{
for(int i =0 ;i<=n;i++) cout << “-“;
}
void printline(int n,char ch)
{
for(int i=0;i<=n; i++) cout << ch;
}
int main()
{
printline();
printline(5);
printline(10, ‘*’);
return 0;
}
|
Polimorfismo
Polimorfismo é uma das características cruciais da POO. Significa simplesmente um nome, múltiplas formas. Já vimos como o conceito de polimorfismo é implementado usando funções e operadores sobrecarregados. As funções membro sobrecarregadas são selecionadas para invocação combinando argumentos, tanto tipo quanto número. O compilador conhece essa informação no tempo de compilação e, portanto, é capaz de selecionar a função apropriada para uma chamada particular no próprio tempo de compilação. Isso é chamado de vinculação antecipada ou vinculação estática ou linkagem estática. Também conhecido como polimorfismo em tempo de compilação, vinculação antecipada simplesmente significa que um objeto é vinculado às suas chamadas de funções no tempo de compilação.
Agora, vamos considerar uma situação onde o nome da função e o protótipo são os mesmos nas classes base e derivadas. Por exemplo, considere as seguintes definições de classe.
| | #include
using namespace std;
class A
{
int x;
public : void show();
};
class B : public A
{
int y;
public : void show();
};
int main()
{
B b;
b.show();
return 0;
} |
Como usamos a função membro show( ) para imprimir os valores dos objetos das classes A e B? Como o protótipo de show( ) é o mesmo em ambos os lugares, a função não é sobrecarregada e, portanto, a vinculação estática não se aplica. Na verdade, o compilador não sabe o que fazer e adia a decisão.
Seria bom se a função membro apropriada pudesse ser selecionada enquanto o programa está em execução. Isso é conhecido como polimorfismo em tempo de execução. Como isso poderia acontecer? O C++ suporta um mecanismo conhecido como função virtual para alcançar o polimorfismo em tempo de execução. Em tempo de execução, quando se sabe quais objetos de classe estão em consideração, a versão apropriada da função é chamada.
Como a função está vinculada a uma classe particular muito depois da compilação, esse processo é denominado vinculação tardia. Também é conhecido como vinculação dinâmica ou linkagem dinâmica porque a seleção da função apropriada é feita dinamicamente em tempo de execução.
3. Tipos de Polimorfismo
Polimorfismo é de dois tipos, a saber.
| 1 | Polimorfismo em tempo de compilação
Ou Vinculação Antecipada
Ou Vinculação Estática
Ou Polimorfismo de Linkagem Estática. Um objeto é vinculado à sua chamada de função em tempo de compilação. |
| 2 | Polimorfismo em tempo de execução
Ou vinculação tardia
Ou Vinculação Dinâmica
Ou Polimorfismo de Linkagem Dinâmica. A seleção e a função apropriada são feitas dinamicamente em tempo de execução. |
| |
|
A vinculação dinâmica é uma das características poderosas do C++. Isso requer o uso de ponteiros para objetos. Discutiremos em detalhes como os ponteiros de objetos e funções virtuais são usados para implementar a vinculação dinâmica.
4. Funções Membro Normais Acessadas com Ponteiros
O programa abaixo consiste em uma classe base
| | / Funções normais acessadas a partir de ponteiro /
/ Polimorfismo com classes (sem usar polimorfismo VIRTUAL /
#include
using namespace std;
class BASE
{
public :
void disp() { cout << “\nVocê está na classe BASE “; }
};
class DERIVED1 : public BASE
{
public :
void disp() { cout << “\nVocê está na classe DERIVED1”; }
};
class DERIVED2 : public BASE
{
public :
void disp() { cout << “\nVocê está na classe DERIVED2”; }
};
int main()
{
DERIVED1 d1; // Objeto da classe derivada 1
DERIVED2 d2; // Objeto da classe derivada 2
BASE *b; // ponteiro para a classe base
b=&d1; // Atribui o endereço de d1 ao ponteiro b
b->disp(); // chamada para disp()
b=&d2; // Atribui o endereço de d2 ao ponteiro b
b->disp(); // chamada para disp()
return 0;
} |
O programa acima demonstra:
| | • Uma classe BASE
• Classes DERIVED1, DERIVED2 derivadas de BASE
• Objetos de classes derivadas (d1,d2)
• Ponteiro da classe BASE b | | | *Saída Você está na classe BASE
Você está na classe BASE |
5. Função Virtual
Virtual significa existente em efeito, mas não na realidade.
Uma função membro pode ser feita como função virtual precedendo a função membro com a palavra-chave virtual.
| | / Polimorfismo com Classes (Polimorfismo Virtual) /
#include
using namespace std;
class B
{
public :
void show(){ cout << “\nclasse B método Show() “; }
virtual void disp() { cout << “\nclasse B método disp()”; }
};
class D : public B
{
public :
void show(){cout << “\nclasse D método Show() “; }
void disp(){ cout << “\nclasse D método disp()”; }
};
int main()
{
D d1;
d1.show();
d1.disp(); // Membro da classe base
B b;
D d;
B *Bptr;
Bptr = &b;
Bptr->show();
Bptr->disp(); // Membro da classe base
Bptr=&d;
Bptr->show(); // membros da classe derivada
Bptr->disp(); // Membro da classe base
return 0;
} | | | Saída método Show() da classe D
método disp() da classe D |
6. Função Pura
Uma função definida em uma classe base e que não tem definição relativa à classe derivada é chamada de função pura. Em palavras simples, uma função pura é uma função virtual sem corpo.
| | #include
using namespace std;
class B
{
public :
void show(){ cout << “\nclasse B método Show() “; }
virtual void disp() = 0; // função virtual pura
};
class D : public B
{
public :
void show(){cout << “\nclasse D método Show() “; }
void disp(){ cout << “\nclasse D método disp()”; }
};
int main()
{
D d1;
d1.show(); // O/P : Método show() da classe D
d1.disp(); // O/P : Método disp() da classe D
D d;
B *Bptr;
Bptr=&d;
Bptr->show(); // O/P : Método show() da classe B
Bptr->disp(); // O/P : Método disp() da classe D
return 0;
} |
Bptr -> show() * é a função executável padrão da Base Bptr -> disp() é a função executável padrão da Base, mas é declarada como uma função virtual pura, então em tempo de execução a disp()* da classe derivada será chamada.
| | / Programa para demonstrar a vantagem das funções virtuais puras /
#include
using namespace std;
enum boolean { false, true };
class NAME
{
protected : char name[20];
public :
void getname()
{ cout << “Digite o nome :”; cin >> name; }
void showname()
{ cout << “\nO nome é “<< name; }
boolean virtual isGradeA() = 0; // função virtual pura
};
class student : public NAME
{
private : float avg;
public :
void getavg()
{
cout << “\nDigite a Média do Estudante :”;
cin >> avg;
}
boolean isGradeA()
{ return (avg>=80) ? true : false ; }
};
class employee : public NAME
{
private : int sal;
public :
void getsal()
{ cout << “\nDigite o salário “; cin >> sal; }
boolean isGradeA()
{ return (sal>=10000) ? true : false ; }
};
int main()
{
NAME names[20]; // número de ponteiros para nome
student s; // ponteiro para estudante
employee *e; // ponteiro para funcionário
int n = 0; // número de Nomes na lista
char choice;
do{
cout << “Digite Estudante ou Funcionário (s/e) “;
cin >> choice;
if(choice==’s’)
{
s = new student; // cria um novo estudante
s->getname();
s->getavg();
names[n++]=s;
}
else
{
e = new employee; // cria um novo funcionário
e->getname();
e->getsal();
names[n++]=e;
}
cout << “Digite outro (y/n) ?”; // faça outro
cin >> choice;
} while(choice==’y’);
for(int j=0; j
names[j]->showname( );
if(names[j]->isGradeA( )==true)
cout << “Ele é uma pessoa do Grau 1”;
}
return 0;
} |
7. Atribuição e Inicialização por Cópia
O compilador C++ está sempre ocupado em seu nome, fazendo coisas que você não se incomoda em fazer. Se você assumir o controle, ele se submeterá ao seu julgamento; caso contrário, ele fará as coisas do seu próprio jeito. Dois exemplos importantes desse processo são o operador de atribuição e o construtor de cópia.
Você já usou o operador de atribuição muitas vezes, provavelmente sem pensar muito sobre isso. Suponha que a1 e a2 sejam objetos. A menos que você diga ao compilador o contrário, a instrução.
a2 = a1; // define a2 para o valor de a1
Causará o compilador copiar os dados de a1, membro por membro, para a2. Esta é a ação padrão do operador de atribuição, =.
Você também está familiarizado com a inicialização de variáveis, inicializando um objeto com outro objeto, como em
alpha a2(a1); // inicializa a2 para o valor de a1
Causa uma ação semelhante. O compilador cria um novo objeto, a2, e copia os dados de a1, membro por membro, para a2. Esta é a ação padrão do construtor de cópia.
Ambas essas atividades padrão são fornecidas, gratuitamente, pelo compilador. Se a cópia membro por membro é o que você deseja, você não precisa tomar mais nenhuma ação. No entanto, se você quiser que a atribuição de inicialização faça algo mais complexo, então você pode substituir as funções padrão. Discutiremos as técnicas para sobrecarregar o operador de atribuição e o construtor de cópia separadamente.
Sobrecarga do Operador de Atribuição
| | // Sobrecarga do Operador de Atribuição ( = )
#include
using namespace std;
class alpha
{
private:
int data;
public:
alpha() { } // construtor sem argumentos
alpha( int d )
{ data = d; } // construtor com um argumento
void display()
{ cout << data; } // Exibir dados
alpha operator =(alpha & a) // operador = sobrecarregado
{
data = a.data; // não feito automaticamente
cout << “\n Operador de atribuição invocado “;
return alpha(data);
}
};
int main()
{
alpha a1(37);
alpha a2;
a2 = a1; // Invoca sobrecarregado =
cout << “\n a2 = “; a2.display(); // exibe a2
alpha a3 = a2; // NÃO invoca =
cout << “\n a3 = “; a3.display(); // exibe a3
return 0;
}
| | | Saída: a2 = 37
a3 = 37 |
8. O CONSTRUTOR DE CÓPIA
Como discutimos, podemos definir e ao mesmo tempo inicializar um objeto para o valor de outro objeto com dois tipos de instrução:
alpha a3(a2); // Inicialização por cópia
alpha a3 = a2; // inicialização por cópia, sintaxe alternativa
Ambos os estilos de definição invocam um construtor de cópia: ou seja, um construtor que copia seu argumento em um novo objeto. O construtor de cópia padrão, que é fornecido automaticamente pelo compilador para cada objeto, realiza uma cópia membro por membro. Isso é semelhante ao que o operador de atribuição faz; a diferença é que o construtor de cópia também cria um novo objeto.
O seguinte exemplo demonstra o construtor de cópia.
| | #include
using namespace std;
class alpha
{
private :
int data;
public:
alpha( ) { } // construtor sem argumentos
alpha(int d) { data = d; } // construtor com um argumento
alpha(alpha& a) // construtor de cópia
{
data = a.data;
cout << “\nConstrutor de cópia invocado”;
}
void display( )
{ cout << data; }
void operator = (alpha& a) // operador = sobrecarregado
{
data = a.data;
cout << “\nOperador de atribuição invocado”;
}
};
int main()
{
alpha a1( 37 );
alpha a2;
a2 = a1; // invoca sobrecarregado =
cout << “ a2 = “; a2.display(); // exibe a2
alpha a3( a1 ); // invoca construtor de cópia
// alpha a3 = a1; // definição equivalente de a3
cout << “ a3 = “; a3.display(); // exibe a3
return 0;
} |
O programa acima sobrecarrega tanto o operador de atribuição quanto o construtor de cópia.
O operador de atribuição sobrecarregado é semelhante ao do exemplo anterior.
9. Ponteiro ‘this’
O C++ usa uma palavra-chave única chamada this para representar um objeto que invoca uma função membro. This é um ponteiro que aponta para o objeto para o qual a função this foi chamada.
Esse ponteiro simplesmente realiza a tarefa de retornar o objeto em si.
O seguinte programa define os objetos i, j e i é atribuído com o valor de 5 e o objeto inteiro de i é atribuído pela sua função membro a j
| | #include
using namespace std;
class A
{
int a;
public:
A() { }
A(int x)
{ a = x; }
void display()
{
cout << a;
}
A get()
{
return *this; // Retornar a si mesmo
}
};
int main()
{
A i(5);
A j;
j = i.get();
j.display();
return 0;
} |
Ref: Programação orientada a objetos em Turbo C++: Robert Lafore
Receba novas postagens na sua caixa de entrada
Sem spam. Cancele a assinatura a qualquer momento.