C++ - Problema com polimorfismo e métodos abstratos

29 views
Skip to first unread message

Kico Weissmann

unread,
Jul 6, 2009, 7:01:18 PM7/6/09
to ccppbrasil
Olá,

estou estudando C++ e estou com uma dúvida referente a polimorfismo e
métodos abstratos.
Para tal, peço para que analisem o código abaixo, pois não consigo
entender o que estou fazendo errado: tenho uma classe abstrata chamada
Shape e duas classes derivadas: Circle e Square, cujas declarações se
encontram abaixo:

#include <iostream>
class Shape {
public:
virtual char* getNome() = 0;

};

class Circle : public Shape {
public:
char* getNome() {
return "Circle";
}
};

class Square : public Shape {
public:
char* getNome() {
return "Square";
}
};

A dúvida na realidade surge no meu método main:

int main(int argc, char **argv) {
Shape *shape[2];

Dúvida 1: por que o código abaixo é inválido?
shape[0] = new Square();
shape[1] = new Circle();
No caso, como eu deveria reescrevê-lo?

Dúvida 2: o código abaixo será executado? Em caso negativo, o que está
errado?
cout << shape[0]->getNome() << endl;
cout << shape[1]->getNome() << endl;
return 1;
}

Márcio Gil

unread,
Jul 7, 2009, 8:03:37 AM7/7/09
to ccppb...@googlegroups.com
> -----Mensagem original-----
> De: Kico Weissmann

>
> Dúvida 1: por que o código abaixo é inválido?
> shape[0] = new Square();
> shape[1] = new Circle();
> No caso, como eu deveria reescrevê-lo?
>

Não tem nada de errado com estas linhas, é um ponteiro para uma classe base
recebendo um objeto derivado.

> Dúvida 2: o código abaixo será executado? Em caso negativo, o que está
> errado?
> cout << shape[0]->getNome() << endl;
> cout << shape[1]->getNome() << endl;

Você deve colocar assim:

std::cout << shape[0]->getNome() << std::endl;
std::cout << shape[1]->getNome() << std::endl;

ou então coloca esta linha logo antes do "main()":

using namespace std;

> return 1;
> }
>

Gianni

unread,
Jul 7, 2009, 8:39:13 AM7/7/09
to ccppb...@googlegroups.com, Kico Weissmann
Faltou declarar as funções das classes derivadas como virtual:

class Circle : public Shape {
public:

virtual char* getNome() {


return "Circle";
}
};
class Square : public Shape {
public:

virtual char* getNome() {
return "Square";
}
};


Acho que é isso...

Rafael Giusti

unread,
Jul 7, 2009, 8:39:08 AM7/7/09
to ccppb...@googlegroups.com
Evite usar a linha

using namespace std;

É "promíscua" demais.

Se preceder todos os nomes com std:: for demais para você, prefira importar apenas os objetos que você pretende utilizar no código:

usign std::cout;
using std::endl;



2009/7/7 Márcio Gil <marci...@bol.com.br>



--
Rafael Giusti
Laboratório de Inteligência Computacional - LABIC
Universidade de São Paulo - USP

Thiago Adams

unread,
Jul 7, 2009, 9:17:02 AM7/7/09
to ccppbrasil
Quando a classe base está definida como virtual, não faz diferença se
as derivadas estão declaradas como virtuais ou não.

-x-

Vendo as dúvidas de um iniciante, é possível ver que C++ é realmente
uma linguagem complicada de se aprender, pois é cheia de detalhes.

Neste exemplo ainda seria importante explicar que o destructor deveria
ser virtual, e como fazer o gerenciamento da memoria alocada.
Ainda assim, provavelmente, a explicação não entraria no detalhe das
exceções nem do detalhe do comportamento do new. (new [] delete []
etc)
Então seria preciso explicar o RAII os containers da STL e os smart
pointers.

Márcio Gil

unread,
Jul 7, 2009, 9:26:15 AM7/7/09
to ccppb...@googlegroups.com
> -----Mensagem original-----
> De: Gianni
Se você declarar na classe base que um método é virtual, este método
continuará sendo considerado virtual mesmo que não tenha a palavra
chave "virtual" nas classes derivadas. Ou seja, "virtual" é opcional
nas classes derivadas pois o compilador já foi instruído a
implementar a virtualização. No entando é bom colocar o "virtual"
nas classes derivadas também para documentar este fato.

Thiago Diniz da Silveira

unread,
Jul 7, 2009, 9:31:46 AM7/7/09
to ccppb...@googlegroups.com
Claro que é bom colocar, depois tu pega um código um pouco maior e tem que ficar lembrando que é virtual. -.- pelo amor de deus deixa a preguiça de lado né!

2009/7/7 Márcio Gil <marci...@bol.com.br>

Jorge Pereira

unread,
Jul 7, 2009, 9:36:54 AM7/7/09
to ccppb...@googlegroups.com, Kico Weissmann
Olá!

Neste caso ai a string retornada e uma constante, então seria interessante retornar "const char*"

[]s
--
Regards,
+--------------------------------------------+
Jorge Pereira,
Blog: http://blog.jorgepereira.com.br/
GPG-Key-ID: 1024D/EC6FB924
+--------------------------------------------+

Henrique Lobo Weissmann

unread,
Jul 7, 2009, 9:36:31 AM7/7/09
to ccppb...@googlegroups.com
He he he, claro, claro.

Eu só perguntei isto porque nos livros de C++ que eu li até agora dizem que só é necessário declarar na classe base.

Então, quando você me disse para incluir a palavra chave virtual nas classes filhas, achei que tivesse me enganado a respeito, e que esta palavra-chave também deveria ser incluida nas classes filhas, entende?

2009/7/7 Thiago Diniz da Silveira <thiag...@gmail.com>



--
Henrique Lobo Weissmann
(55) 31 9226-0459

Gianni

unread,
Jul 7, 2009, 9:41:34 AM7/7/09
to ccppb...@googlegroups.com

Então, eu disse "Acho" por que justamente tive um problema semelhante, que eu
não lembro qual foi a solução, mas se não me engano foi com algo assim:
cupostamente deveria funcionar de forma X mas o compilador fazia Y. Não tenho
certeza mesmo, só dei um palpite de intrometido mesmo. ;-)

Henrique Lobo Weissmann

unread,
Jul 7, 2009, 9:45:33 AM7/7/09
to ccppb...@googlegroups.com
Faz todo o sentido!

Observei que quando vou verificar o tipo armazenado na matriz usando o operador idtype o valor retornado é Shape, e não Circle ou Square.

Nestes casos, estou REALMENTE definindo corretamente os valores dos meus apontadores?

No caso, estou usando o Dev-C++ e o compilador é o gcc via mingw

2009/7/7 Gianni <nasus....@gmail.com>

Thiago Diniz da Silveira

unread,
Jul 7, 2009, 10:01:03 AM7/7/09
to ccppb...@googlegroups.com
É opcional, mas como eu disse e outros disseram é quase obrigatório colocar né hehe =D
Desculpa o nervosismo na resposta Henrique. ;)

Abraço.

2009/7/7 Gianni <nasus....@gmail.com>

Henrique Lobo Weissmann

unread,
Jul 7, 2009, 10:08:38 AM7/7/09
to ccppb...@googlegroups.com
no problem!

2009/7/7 Thiago Diniz da Silveira <thiag...@gmail.com>
É opcional, mas como eu disse e outros disseram é quase obrigatório colocar né hehe =D

Pedro Lamarão

unread,
Jul 8, 2009, 3:40:59 PM7/8/09
to ccppbrasil
On 6 jul, 20:01, Kico Weissmann <loboweissm...@gmail.com> wrote:

> A dúvida na realidade surge no meu método main:
>
> int main(int argc, char **argv) {
>         Shape *shape[2];
>
> Dúvida 1: por que o código abaixo é inválido?
>         shape[0] = new Square();
>         shape[1] = new Circle();
> No caso, como eu deveria reescrevê-lo?

Boa pergunta.

Por que você acha que esse código é inválido?
Qual é o problema, o compilador está falhando a compilação?

Perguntas do tipo "o código está certo? em caso negativo, bla bla bla"
são perguntas de trabalho de casa da faculdade.

--
P.

Henrique Lobo Weissmann

unread,
Jul 8, 2009, 3:51:43 PM7/8/09
to ccppb...@googlegroups.com
Oi Pedro,

O que observei foi o seguinte. Na minha matriz, que inicialmente eu pré carregava como ponteiros para a classe basica, eu a populava tal como no código abaixo.

No entanto, toda vez que ia executar um método abstrato nas classes filhas, dava erro de execução.
Ao verificar o tipo armazenado na matriz usando o operador idtype, o que verifiquei é que lá estavam instâncias da classe base, e não das classes filhas.
Como consequencia, toda vez que eu chamava o método abstrato, este não estava implementado na instancia armazenada e eu tinha um erro de execução.

Esta é a razão pela qual eu achei que o código estava inválido de alguma forma. Como pode ver, nada a ver com trabalhos de faculdade ou coisas do gênero.
Na realidade, é mais relacionado a um hobby que iniciei neste final de semana e cujo resultado estou documentando no meu blog:
http://www.itexto.net/devkico/?p=419

Sendo assim, pode ficar tranquilo :) : não é mais um estudante pedindo para outros fazerem seu trabalho de faculdade, mas sim alguém que DE FATO está tentando se aprofundar na linguagem C++ e que acreditou que talvez a comunidade brasileira poderia lhe auxiliar neste processo.

Um abraço

2009/7/8 Pedro Lamarão <pedro....@member.fsf.org>

Bruno Sanches

unread,
Jul 8, 2009, 3:57:54 PM7/8/09
to ccppb...@googlegroups.com
Ola Henrique,

seu código esta correto, o que deve estar errado é que algum metodo que deveria ser virtual e não é.

Pode mostrar a declaração (e a implementação se não for muito grande) do método abstrato na classe base e nas classes derivadas?

Bruno Sanches
========================
http://bcsanches.wordpress.com


2009/7/8 Henrique Lobo Weissmann <lobowe...@gmail.com>

Pedro Lamarão

unread,
Jul 8, 2009, 5:45:43 PM7/8/09
to ccppbrasil
On 8 jul, 16:51, Henrique Lobo Weissmann <loboweissm...@gmail.com>
wrote:

> O que observei foi o seguinte. Na minha matriz, que inicialmente eu pré
> carregava como ponteiros para a classe basica, eu a populava tal como no
> código abaixo.
>
> No entanto, toda vez que ia executar um método abstrato nas classes filhas,
> dava erro de execução.
> Ao verificar o tipo armazenado na matriz usando o operador idtype, o que
> verifiquei é que lá estavam instâncias da classe base, e não das classes
> filhas.
> Como consequencia, toda vez que eu chamava o método abstrato, este não
> estava implementado na instancia armazenada e eu tinha um erro de execução.

Acho que percebi a questão.
É algo do C++ que eu ainda não parei pra aprender completamente.

Nas classes derivadas de Shape, não há uma função declarada com a
palavra-chave "virtual".
Por esse motivo, o compilador deve estar considerando que essas
classes não são polimórficas, e não produzindo coisas como um ponteiro
para vtable e coisas assim.

Por acaso a presença de um "virtual" na função sobrescrita nas classes
derivadas resolve o problema?

Em geral esse tipo de situação não acontece porque invariavelmente o
destruidor é "virtual".

--
P.

Eric Chiesse

unread,
Jul 8, 2009, 11:12:18 PM7/8/09
to ccppb...@googlegroups.com
Henrique, estava escrevendo um exemplo enorme com código fonte compilável e tudo para servir de exemplo, quando me ocorreu que a tua dúvida é bem mais simples.

Você deve estar tentando algo como:

cout << typeid(shape[0]).name() << endl;

E está obtendo "Shape *" como resposta.

Tenta usar:
cout << typeid(*shape[0]).name() << endl; // Repare no asterisco antes de shape.

Você deve obter o tipo que você deseja ver.

Abraço.

Eric.

Henrique Lobo Weissmann

unread,
Jul 9, 2009, 7:22:55 AM7/9/09
to ccppb...@googlegroups.com
Nope,

ai que tá Eric, eu fazia exatamente como você mostrou, com o * na frente, e o resultado obtido ainda era do tipo Shape.

Estou no trabalho agora, porém assim que chegar em casa irei copiar o código fonte para que vocês dêem uma olhada, porque realmente o resultado que obtive é muito estranho.


2009/7/9 Eric Chiesse <echi...@gmail.com>

Jorge Pereira

unread,
Jul 9, 2009, 9:45:52 AM7/9/09
to ccppb...@googlegroups.com
Copie e coloque no http://pastebin.ubuntu.com e nos envie a URL


[]s
--
Regards,
+--------------------------------------------+
Jorge Pereira,
Blog: http://blog.jorgepereira.com.br/
GPG-Key-ID: 1024D/EC6FB924
+--------------------------------------------+


2009/7/9 Henrique Lobo Weissmann <lobowe...@gmail.com>

Guilherme Longo

unread,
Jul 9, 2009, 10:20:39 PM7/9/09
to ccppb...@googlegroups.com
Pessoal, bom dia!
Estou muito perto de concluir meu projeto mas estou atolado em um detalhe.

Escrevi uma aplicação usando as api do alsa para gravar e reproduzir
som. Junto, eu estou implementando uma analize de fourier, usando a
biblioteca fftw: http://www.fftw.org/!

Ai que está o problema. É utilizado um ring buffer tanto para leitura
quanto para escrita.
Eu preciso fazer com que esse IN sejá um ponteiro para meu ring buffer.
Algêm pode me dar uma ajuda.

O código está assim:

void capture_go(int fd, off64_t count, int rtype, char *name)
{
size_t c;
off64_t cur;
ssize_t r, err;
* fftw_complex *in, *out;
fftw_plan p;

** in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * 44100);
out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * 44100);*

* in = audiobuf; *********(isso aqui que eu não consigo
fazer)*************

header(rtype, name);
set_params();

* p = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);

* do {
for (cur = count; cur > 0; cur -= r) {
c = (cur <= chunk_bytes) ? cur : chunk_bytes;
c = c * 8 / bits_per_frame;
if ((size_t)(r = pcm_read(audiobuf, c)) != c)
*
*
break;
r = r * bits_per_frame / 8;
if ((err = write(fd, audiobuf, r)) != r) {
perror(name);
exit(EXIT_FAILURE);
}
if (err > 0)
fdcount += err;
}
} while (rtype == FORMAT_RAW && !timelimit);
}


Muito Obrigado

Eric Chiesse

unread,
Jul 9, 2009, 10:29:17 PM7/9/09
to ccppb...@googlegroups.com
Guilherme, você poderia criar um tópico novo para esse assunto ?

Abraço.

Eric.

2009/7/9 Guilherme Longo <grlongo...@gmail.com>

Guilherme Longo

unread,
Jul 10, 2009, 5:45:09 AM7/10/09
to ccppb...@googlegroups.com

wander

unread,
Jul 10, 2009, 6:35:33 AM7/10/09
to ccppbrasil
Olá Kico,

Qual o compilador e versão você está usando? Eu rodei no g++ 4.3.2 e
funcionou perfeitamente,
segue abaixo o código (com algumas modificações para evitar warnings e
com uma destrutora virtual):

#include <iostream>
class Shape {
public:
virtual const char* getNome() = 0;
virtual ~Shape() {};

};

class Circle : public Shape {
public:
const char* getNome() {
return "Circle";
}
};

class Square : public Shape {
public:
const char* getNome() {
return "Square";
}

};

int main() {
using std::cout;
using std::endl;
Shape *shape[2];
shape[0] = new Square();
shape[1] = new Circle();
cout << shape[0]->getNome() << endl;
cout << shape[1]->getNome() << endl;
delete shape[0];
delete shape[1];
return 1;
}

Saída:

Square
Circle

Wander

On 6 jul, 20:01, Kico Weissmann <loboweissm...@gmail.com> wrote:

Eric Chiesse

unread,
Jul 10, 2009, 8:08:37 AM7/10/09
to ccppb...@googlegroups.com
Henrique, tá estranho mesmo então. Testei esse comportamento no visual studio e MinGW no Windows e tb no gcc no Ubuntu e em todos estava foi correto.

Sem o exato código fonte que você está usando é difícil dizer qq coisa.

Abraço.

Eric.

2009/7/9 Henrique Lobo Weissmann <lobowe...@gmail.com>
Nope,

Guilherme Longo

unread,
Jul 10, 2009, 1:02:09 PM7/10/09
to ccppb...@googlegroups.com
Pessoal, estou linkando uma biblioteca fftw da seguinte maneira:

bash-3.1$ gcc -g -I/usr/local/include -L/usr/local/lib -lfftw3
-lasound aplay.c -o gravar

E estou encontrando o seguinte erro:

bash-3.1$ gcc -g -I/usr/local/include -L/usr/local/lib -lfftw -lasound
aplay.c -o gravar
aplay.c: In function 'set_params':
aplay.c:254: warning: 'snd_pcm_sw_params_get_xfer_align' is deprecated
(declared at /usr/include/alsa/pcm.h:1112)
aplay.c:261: warning: 'snd_pcm_sw_params_set_sleep_min' is deprecated
(declared at /usr/include/alsa/pcm.h:1113)
aplay.c:289: warning: 'snd_pcm_sw_params_set_xfer_align' is deprecated
(declared at /usr/include/alsa/pcm.h:1111)
aplay.c:797:2: warning: no newline at end of file
/usr/lib/gcc/i486-slackware-linux/4.2.4/../../../../i486-slackware-linux/bin/ld:
cannot find -lfftw
collect2: ld returned 1 exit status


Gostaria de saber se estou fazendo a linkagem certa... essa biblioteca
deveria estar dentro da /usr/local/lib não é?
Uso slack 12.2

Muito obrigado

Paulo Rogério Panhoto

unread,
Jul 10, 2009, 3:42:23 PM7/10/09
to ccppb...@googlegroups.com
Você já procurou pela libfftw....so no diretório /usr/local/lib?

2009/7/10 Guilherme Longo <grlongo...@gmail.com>

marcos machado

unread,
Jul 10, 2009, 3:48:59 PM7/10/09
to ccppb...@googlegroups.com
verifique se o caminho /usr/local/lib está definido e se a lib está lá.. se a lib estiver no diretório mas o caminho não estiver definido como local de libs padrão tente, antes de compilar, o comando abaixo
$ export LD_LIBRARY_PATH=-/usr/local/lib


2009/7/10 Paulo Rogério Panhoto <pa...@voicetechnology.com.br>

Paulo Rogério Panhoto

unread,
Jul 10, 2009, 3:53:21 PM7/10/09
to ccppb...@googlegroups.com
Dependendo da versão que você está usando, a biblioteca tem um nome:

v2.x:
http://repository.slacky.eu/slackware-12.2/libraries/fftw/2.1.5/
você especifica -lfftw (referente a libfftw.a)

v3.x
http://repository.slacky.eu/slackware-12.2/libraries/fftw/3.2.1/
você eespecifica -lfftw3 (referente a libfftw3.a)

2009/7/10 marcos machado <solene...@gmail.com>

Guilherme Longo

unread,
Jul 10, 2009, 6:05:15 PM7/10/09
to ccppb...@googlegroups.com
Pessoal, não estava funcionando de jeito nenhum.
Mas encontrei o pacote compilado para o slack.

Ai deu certo só com a linkagem que eu tava fazendo ;o)

Muito obrigado

Alias eu gostaria de discutir um assunto dobre o fftw mas não encontrei
nenhum fórum ou chat para isso. Alguêm aqui em esperiência com fftw e
poderia tentar me ajudar em uma questão em pvt?

Muito obrigado
Paulo Rogério Panhoto wrote:
> Dependendo da versão que você está usando, a biblioteca tem um nome:
>
> v2.x:
> http://repository.slacky.eu/slackware-12.2/libraries/fftw/2.1.5/
> você especifica -lfftw (referente a libfftw.a)
>
> v3.x
> http://repository.slacky.eu/slackware-12.2/libraries/fftw/3.2.1/
> você eespecifica -lfftw3 (referente a libfftw3.a)
>
> 2009/7/10 marcos machado <solene...@gmail.com
> <mailto:solene...@gmail.com>>
>
> verifique se o caminho /usr/local/lib está definido e se a lib
> está lá.. se a lib estiver no diretório mas o caminho não estiver
> definido como local de libs padrão tente, antes de compilar, o
> comando abaixo
> $ export LD_LIBRARY_PATH=-/usr/local/lib
>
>
> 2009/7/10 Paulo Rogério Panhoto <pa...@voicetechnology.com.br
> <mailto:pa...@voicetechnology.com.br>>
>
> Você já procurou pela libfftw....so no diretório /usr/local/lib?
>
> 2009/7/10 Guilherme Longo <grlongo...@gmail.com
> <mailto:grlongo...@gmail.com>>
Reply all
Reply to author
Forward
0 new messages