Não consigo criar meu programa com polimorfismo e herança. [RESOLVIDO]

1. Não consigo criar meu programa com polimorfismo e herança. [RESOLVIDO]

skjdeecedcnfncvnrfcnrncjvnjrnfvjcnjrjvcjrvcj
Londreslondres

(usa Parabola)

Enviado em 14/07/2022 - 10:18h

Olá. Estou tentando criar um programinha com polimorfismo e herança no C++

As classes carro e moto herdam de automovel.

Arquivo principal.cpp (o programinha começa aqui)
#include <iostream>
#include "automovel.hpp"
#include "carro.hpp"
#include "moto.hpp"

using namespace std;

int main(){
Automovel *automovel = new Carro();
automovel->setjanelas(4);
cout << automovel->getjanelas() << endl;
return 0;
}


Arquivo automovel.hpp
#include <iostream>

using namespace std;

class Automovel {
public:
int rodas, janelas;
float combustivel, velocidade;
bool ligado;
void setrodas(int rodas);
void setjanelas(int janelas);
void setcombustivel(float combustivel);
void setvelocidade(float velocidade);
void setligado(bool ligado);
int getrodas();
int getjanelas();
float getcombustivel();
float getvelocidade();
bool getligado();
};


Arquivo automovel.cpp
#include <iostream>
#include "automovel.hpp"

void Automovel::setrodas(int rodas){
this->rodas = rodas;
}
void Automovel::setjanelas(int janelas){
this->janelas = janelas;
}
void Automovel::setcombustivel(float combustivel){
this->combustivel = combustivel;
}
void Automovel::setvelocidade(float velocidade){
this->velocidade = velocidade;
}
void Automovel::setligado(bool ligado){
this->ligado = ligado;
}
int Automovel::getrodas(){
return this->rodas;
}
int Automovel::getjanelas(){
return this->janelas;
}
float Automovel::getcombustivel(){
return this->combustivel;
}
float Automovel::getvelocidade(){
return this->velocidade;
}
bool Automovel::getligado(){
return this->ligado;
}


Arquivo carro.hpp
class Carro: public Automovel
{
public:
Carro();
};


Arquivo carro.cpp
#include "automovel.hpp"
#include "carro.hpp"

Carro::Carro(){
rodas = 4;
janelas = 4;
combustivel = 10.0;
velocidade = 0;
ligado = false;
}


Arquivo moto.hpp
class Moto: public Automovel
{
public:
Moto();
};


Arquivo moto.cpp
#include "automovel.hpp"
#include "moto.hpp"

Moto::Moto(){
rodas = 2;
janelas = 0;
combustivel = 2.0;
velocidade = 10000;
ligado = true;
}


Quando vou compilar:
$ g++ automovel.cpp automovel.hpp carro.cpp carro.hpp moto.cpp moto.hpp principal.cpp -o programa                                    
carro.hpp:2:1: error: expected class-name before ‘{’ token
2 | {
| ^
moto.hpp:2:1: error: expected class-name before ‘{’ token
2 | {
| ^
$


Dá erro ao compilar, pois eu errei em alguma coisa.
Gostaria de saber qual foi o erro, e como posso corrigi-lo?


  


2. Re: Não consigo criar meu programa com polimorfismo e herança. [RESOLVIDO]

Samuel Leonardo
SamL

(usa XUbuntu)

Enviado em 14/07/2022 - 16:55h

Deve ser porque você tá tentando compilar tudo de uma vez, digo, o g++ não compila nenhum dos arquivos passado no terminal.
O que você deve fazer é compilar em separado cada um deles.
Por exemplo, os arquivos automovel.cpp e automovel.hpp são chamados também de unidades de compilação (compiler unit).
Isso significa na prática que o compilador espera que você os compile de forma separada.
Isso é muito útil em projetos maiores.

Pra compilar uma classe que está definida num hpp e o código num cpp (unidade de compilação), basta fazer:
g++ -c arquivoClasse.cpp 

Isso vai compilar o arquivoClasse.cpp que você passar e criar um arquivoClasse.o, que é um arquivo de código objeto que serve, a grosso modo, pra você colocar na linha de comando quando for compilar o programa.

Sendo assim, você deve compilar assim:
g++ -c automovel.cpp
g++ -c carro.cpp
g++ -c moto.cpp

NOTA: sempre que modificar um arquivo .hpp ou .cpp deverá executar um dos comandos acima para o arquivo que foi mudado.
Por exemplo, se dos 3 arquivos eu mudei o código do automovel, então, deverá compilar toda a classe do carro e moto e do automovel também.

E linkar tudo, basta fazer:
g++ -o programa principal.cpp automovel.o carro.o moto.o 

Observação:
sempre coloque os arquivos objetos no final do comando de compilação porque alguns compiladores exigem isso, como o próprio g++, que as vezes buga por isso.

Pra não parecer tão maçante ficar compilando na mão, procure estudar sobre arquivos Makefile. Daí vai precisar apenas executar um make dentro da pasta atual toda vez que modificar o código do programa e ele vai compilar.


3. Re: Não consigo criar meu programa com polimorfismo e herança. [RESOLVIDO]

leandro peçanha scardua
leandropscardua

(usa Ubuntu)

Enviado em 14/07/2022 - 20:07h


SamL escreveu:

Deve ser porque você tá tentando compilar tudo de uma vez, digo, o g++ não compila nenhum dos arquivos passado no terminal.
O que você deve fazer é compilar em separado cada um deles.
Por exemplo, os arquivos automovel.cpp e automovel.hpp são chamados também de unidades de compilação (compiler unit).
Isso significa na prática que o compilador espera que você os compile de forma separada.
Isso é muito útil em projetos maiores.

Pra compilar uma classe que está definida num hpp e o código num cpp (unidade de compilação), basta fazer:
g++ -c arquivoClasse.cpp 

Isso vai compilar o arquivoClasse.cpp que você passar e criar um arquivoClasse.o, que é um arquivo de código objeto que serve, a grosso modo, pra você colocar na linha de comando quando for compilar o programa.

Sendo assim, você deve compilar assim:
g++ -c automovel.cpp
g++ -c carro.cpp
g++ -c moto.cpp

NOTA: sempre que modificar um arquivo .hpp ou .cpp deverá executar um dos comandos acima para o arquivo que foi mudado.
Por exemplo, se dos 3 arquivos eu mudei o código do automovel, então, deverá compilar toda a classe do carro e moto e do automovel também.

E linkar tudo, basta fazer:
g++ -o programa principal.cpp automovel.o carro.o moto.o 

Observação:
sempre coloque os arquivos objetos no final do comando de compilação porque alguns compiladores exigem isso, como o próprio g++, que as vezes buga por isso.

Pra não parecer tão maçante ficar compilando na mão, procure estudar sobre arquivos Makefile. Daí vai precisar apenas executar um make dentro da pasta atual toda vez que modificar o código do programa e ele vai compilar.


Complementando: crie um makefile para fazer a compilação ou um script



4. Re: Não consigo criar meu programa com polimorfismo e herança.

Paulo
paulo1205

(usa Ubuntu)

Enviado em 14/07/2022 - 23:44h

As sugestões acima estão OK, mas a invocação que você fez ao g++ não está errada, só não é muito eficiente, porque a ideia de separar a programação em múltiplos arquivos diferentes é justamente poder compilar as partes que dependam de alterações no código fonte desde a última compilação.

O problema mesmo no que você fez foi o fato de que dentro de carro.hpp e moto.hpp você menciona Automovel, mas não a apresenta ao compilador. A declaração dessa classe reside em automovel.hpp, então você vai ter de incluir (ou importar, agora com o C++-20) tal declaração antes de usá-la, quer como classe base dessas suas declarações de classes derivadas, quer para instanciar objetos.

O simples fato de você colocar todos os arquivos .cpp na linha de comando do g++ não faz automaticamente com que cada arquivo compilado informe ao próximo arquivo todos os símbolos que ele reconheceu ao longo da compilação. Cada novo arquivo .cpp inicia uma compilação nova, de modo que todos os símbolos que forem necessários em cada uma dessas compilações mas não sejam diretamente declarados nesses arquivos requererão um #include (ou import) do arquivo onde as respectivas declarações ocorrerem.


Dito isso, notei nos seus arquivos que você não usou a técnica de include guarding para se proteger contra a múltipla inclusão do mesmo arquivo várias vezes na mesma compilação. Recordando brevemente como fazer, todos os seus arquivos de cabeçalhos, tanto em C quanto em C++, devem ter a seguinte forma geral.
// Exemplo: arquivo “exemplo.h”
#ifndef EXEMPLO_H_GUARD__ // Testa se o símbolo de guarda está indefinido.
#define EXEMPLO_H_GUARD__ // Define o símbolo de guarda, de modo a evitar que novas inclusões do mesmo arquivo dentro da mesma compilação provoque o aparecimento de declarações repetidas.

/* Suas declarações e definições de tipos vão aqui. */

#endif // Fecha o bloco de compilação condicional baseada no símbolo de guarda.


Você deve aplicar isso a todos os seus cabeçalhos (no caso, a automovel.hpp, carro.hpp e moto.hpp).


Ainda dois outros problemas. O primeiro, mais estético do que funcional, é que você incluiu <iostream> dentro de automovel.hpp, mas aparentemente não usou nada que justificasse tal inclusão, de modo que provavelmente seria melhor não a fazer.

O segundo, um pouco mais grave, é que, provavelmente por causa da inclusão de <iostream>, você colocou uma diretiva using namespace std; dentro do cabeçalho, o que geralmente não é uma boa ideia, porque pode acabar poluindo o restante da compilação com símbolos que não são necessários e podem até causar problemas em determinadas situações. Normalmente, quando você usa objetos ou tipos de dados declarados em outro cabeçalho dentro de um arquivo de cabeçalhos seu, emprega sempre seus nomes completamente qualificados, como mostra o exemplo abaixo, de um arquivo x.h que declara uma classe que depende do tipo std::string, declarado dentro de <string>.
#ifndef X_H__
#define X_H__

#include <string>

class X {
private:
std::string value;

public:
X(const std::string &);

inline std::string get(){ return value; }
};

#endif // !defined(X_H__)


A observação acima vale para o arquivo de cabeçalho; a implementação, possivelmente num arquivo x.cc, poderia usar a diretiva using namespace sem problemas (desde que alguém não resolva chamar x.cc dentro de um #include, o que é tecnicamente possível, mas provavelmente errado).
// Arquivo x.cc: implementação da classe X.

#include <string>
#include "x.h"

using namespace std;

X::X(const string &s): value(s) { }



Ainda mais uma observação: você falou em polimorfismo, mas suas classes não tem funções-membros declaradas como virtual.

É verdade que o termo “polimorfismo” tem mais de um sentido possível, dependendo do contexto. Entretanto, quando se fala de OO, e de C++ em particular, o efeito desejado é permitir que uma referência ou ponteiro para um objeto de uma classe base adquira comportamentos diferenciados quando fizer referência a um objeto de uma classe derivada. Com essa finalidade, o C++ requer que as funções-membros sujeitas a comportamento polimórfico sejam especificadas como virtuais.

Veja o seguinte exemplo.
#include <iostream>

struct automovel {
void barulho() const { std::cout << "[barulho genérico]\n"; }
};

struct carro: public automovel {
void barulho() const { std::cout << "vruum, vruum\n"; }
};

struct motoca: public automovel {
void barulho() const { std::cout << "pó-ró-pó-pó-pó-pó\n"; }
};


int main(){
automovel *autos[]={ new automovel, new carro, new motoca };
for(auto x: autos)
x->barulho();
}

O programa acima vai imprimir três vezes “[barulho genérico]”. Não vai haver polimorfismo, embora os ponteiros tenham sido criados para classes derivadas, porque a função barulho não foi declarada como virtual na classe base.

Se você mudar apenas a declaração de automovel::barulho para incluir a especificação de que a função-membro é virtual, o polimorfismo vai aparecer.
struct automovel {
virtual void barulho() const { std::cout << "[barulho genérico]\n"; }
};

$ g++ x.cc
$ ./a.out
[barulho genérico]
vruum, vruum
pó-ró-pó-pó-pó-pó


Muitas vezes (provavelmente a maioria esmagadora das vezes), porém, quando você tem uma classe base polimórfica, você quer obrigar todas as instâncias a serem de uma classe derivada, com a classe base servindo apenas como uma referência genérica para objetos de tipos correlatos mas não necessariamente idênticos. Nesse caso, uma ou mais funções virtuais da classe base são definidas não com um corpo, mas com valor igual a zero, chamando-se “funções-membros virtuais puras” (pure virtual member functions), e a classe passa a ser classificada como classe abstrata, pois não se podem criar objetos dessa classe, mas tão-somente de classes derivadas que possuam suas próprias reimplementações de todas as funções virtuais puras da classe base.

No nosso exemplo, para transformar a classe automovel em abstrata, eis como se faria.
struct automovel {
virtual void barulho() const = 0;
};


Obviamente, tal alteração faria com que o primeiro elemento usado nas versões anteriores do programa no vetor autos, dentro de main(), não pudesse mais ser declarado, pois new automovel passa a ser uma tentativa inválida de instanciar uma classe abstrata.


Funções virtuais explícitas do C++ podem causar alguma confusão inicial na cabeça de quem vem de experiência anterior com Java ou similares. Isso porque em Java todos os objetos de qualquer classe são necessariamente referências (i.e. ponteiros), e todas as funções-membros são implicitamente polimórficas (aquilo que o C++ chama de funções-membros virtuais).

Para implementar funções virtuais (no caso do Java ou similares, todas as funções-membro de qualquer classe), é necessário fazer uso interno de ponteiros, com uma tabela de ponteiros com uma entrada para cada função virtual. Esse uso de ponteiros pode acarretar algum impacto de desempenho, ao se comparar uma função virtual com uma que não seja virtual (cuja chamada ao endereço de destino pode ser embutida diretamente no código executável, em vez de se consultar a tabela primeiro, e só depois executar a chamada). Por esse motivo, o C++ permite distinguir funções-membros não-virtuais e virtuais, e é recomendável usar não-virtuais sempre em que o polimorfismo não for estritamente necessário para tais funções.


... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)


5. Re: Não consigo criar meu programa com polimorfismo e herança. [RESOLVIDO]

skjdeecedcnfncvnrfcnrncjvnjrnfvjcnjrjvcjrvcj
Londreslondres

(usa Parabola)

Enviado em 15/07/2022 - 14:30h

Muito obrigado a todos que me responderam.

Consegui resolver o meu problema com as dicas de SamL, porém a explicação de paulo1205 também me foi muito útil.

Comecei a programar em Java, por isso segui a mesma linha de raciocínio que eu usava no Java. Decidi aprender a programar em C++, porque é uma linguagem de programação com maior integração com o sistema operacional.







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts