"My software never has bugs. It just develops random features."
-Unknown
Alguns dos códigos fontes podem ser encontrados no arquivo: cpp.zip
Antes de mais nada, o enunciado do exercício:
A empresa Horrores S/A é especializada na venda de produtos de entretenimento. Ela oferece aos clientes uma ampla gama de produtos de música, cinema e literatura. Todos os seus produtos devem ficar cadastrados em um catálogo de produtos. Considere que a classe Catalogo possa armazenar até 100 produtos e tenha os seguintes métodos e atributos: o construtores e um destrutor adequado; o insereProduto - um método de inserção de produtos; o excluiProduto - um método de exclusão de produtos; o mostraDetalhes - um método que imprime na tela os atributos de todos os produtos; (a) Faça a declaração a classe Catalogo de acordo com as boas práticas da POO; (b) Declare a classe Produto com as seguintes funcionalidades: o Os métodos e atributos necessários; o Um método de entrada dos dados; o Um atributo preço; o Um método que mostre os atributos do produto na tela; (c) Declare as seguintes classes usando herança: o Cinema, com as seguintes características: - Os produtos de Cinema têm, código, descriçção, artista principal, gênero (drama, comédia, ação...) e mídia: 1 - fita , 2 - DVD o Audio, com as seguintes características: - Os produtos de Audio têm código, descriçãoo, artista principal e um atributo mídia que pode ser: 1 - CD, 2 - Vinil; o Literatura, com as seguintes características: - Os produtos de Literatura têm, código, descrição e número de páginas.
Alguns métodos eu não implementei por falta de tempo:
/* void insereProduto( Produto *ip ); void excluiProduto( Produto *ep ); void mostraDetalhes(); */
A classe exemplifica o uso do polimorfismo: Para o construtor da classe Catalogo, passo apenas um objeto Produto. Com isso, implemento apenas um construtor.
#include <string> using std::string; //#################################################################### //Classe base //#################################################################### class Produto { public: Produto( int , string ); int getCodigo(){ return codigo ; }; string getDescricao(){ return descricao; }; void setCodigo( int c ){ codigo = c;}; void setDescricao( string Des ){ descricao = Des ;}; void setPreco( int p ){ preco = p ; }; int GetPreco(){ return preco ; }; virtual int getTipo() {}; virtual string getArtista(){}; virtual string getGenero(){}; virtual int getMidia(){}; virtual int getNoPag(){}; protected: int preco; int codigo; string descricao; }; Produto::Produto( int c , string descr ) { setCodigo( c ); setDescricao( descr ); } //#################################################################### //Classe Cinema //#################################################################### class Cinema : public Produto { public: Cinema( int , string, string , string , int ); string getArtista(){ return artista; }; string getGenero(){ return genero; }; int getMidia(){ return midia ; }; void setArtista( string a ){ artista = a ;}; void setGenero( string g ){ genero = g ;}; void setMidia( int m ) { midia = m ;}; int getTipo() { return 0 ; }; protected: string artista; string genero; int midia; // 1 fita 2 DVD }; Cinema::Cinema( int c , string descr, string artis, string gen, int mi ):Produto( c, descr ){ setArtista( artis ); setGenero( gen ); setMidia( mi ); } //#################################################################### //Clase Audio //#################################################################### class Audio : public Produto { public: Audio( int , string , string , int ); string getArtista(){ return artista; }; int getMidia(){ return midia ; }; void setArtista( string a ){ artista = a ;}; void setMidia( int m ) { midia = m ;}; int getTipo() { return 1 ; }; protected: string artista; int midia; // 1 CD 2 Vinil }; Audio::Audio( int c , string descr, string artis, int mi ):Produto( c, descr ){ setArtista( artis ); setMidia( mi ); } //#################################################################### //Literatura //#################################################################### class Literatura: public Produto { public: Literatura( int , string , int ); int getTipo() { return 2 ; }; int getNoPag() { return n_opaginas ; }; void setNoPag( int n_o ){ n_opaginas = n_o ; }; private: int n_opaginas; }; Literatura::Literatura( int c , string descr, int np ):Produto( c , descr){ setNoPag( np ); } //#################################################################### //Classe Catalogo // 0------------------- CINEMA // 1------------------- AUDIO // 2------------------- LITERATURA //#################################################################### class Catalogo { public: Catalogo( Produto *p[] , int n ); ~Catalogo(); /* void insereProduto( Produto *ip ); void excluiProduto( Produto *ep ); void mostraDetalhes(); */ int getNoRegis(){ return noregis ; }; void setNoRegis( int n_ ) { noregis = n_ ; }; Produto *myp[100]; private: int noregis; }; Catalogo::Catalogo( Produto *p[] , int n ){ int i; setNoRegis( n ); for ( i = 0 ; i < n ; i++ ){ switch ( p[i]->getTipo() ) { case 0 : //CINEMA myp[i] = new Cinema( p[i]->getCodigo(), p[i]->getDescricao(), p[i]->getArtista(), p[i]->getGenero(), p[i]->getMidia() ); break; case 1 : //AUDIO myp[i] = new Audio( p[i]->getCodigo(), p[i]->getDescricao(), p[i]->getArtista(), p[i]->getMidia() ); break; case 2 : //LITERATURA myp[i] = new Literatura( p[i]->getCodigo(), p[i]->getDescricao(), p[i]->getNoPag() ); break; default: //ERRO break; } } } Catalogo::~Catalogo(){ int i; for ( i = 0 ; i < getNoRegis() ; i++ ){ delete myp[i]; }; };
O guru ZeRo está fazendo um curso de C++. O professor do cara pediu que ele fizesse um programa para estudar destrutores. Mas o exemplo era muito longo e envolvia muitas classes. Por isso, resolvi criar meu exemplo mais simples ainda....
Basicamente, em C++ você aloca memória com o operador new. Você desaloca memória com delete. Ou seja:
MinhaClasse * cl = new MinhaClasse();
Com isso, nosso compilador aloca no Heap uma quantidade de memória suficiente para instanciar um objeto. Quando finalizado, você deve liberar essa memória. Em Java ou C#, há um coletor de resíduos chamado "garbage collector". Ele se incumbe de desalocar essa área de memória. Infelizmente em C++ isso não acontece. Claro, em exemplos simples não faz diferença, mas imagine que você esteja trabalhando com imagens ou objetos pesados em geral. Assim:
delete cl;
Manda para o "mundo dos sonhos" a memória que você alocou. Logo, o construtor estabelece a fundação e inicializa. O destrutor é o agente do caos e da destruição.
#include <iostream> using std::cout; using std::endl; //classe Peao class Peao { public: Peao( int H ) { h = H; }; int getH(){ return h ; }; private: int h; }; //classe Horrores class Horrores{ public: Horrores( Peao *p);//CONSTRUIR ~Horrores();//DESTRUIR private: Peao *piao; // piao eh um objeto }; //construtor recebe como parâmetro um objeto peao Horrores::Horrores( Peao *p ){ piao = new Peao( p->getH() ); //CONSTRUIR } Horrores::~Horrores(){ delete piao;// piao enviado ao espaço//DESTRUIR } int main() { Peao *pp = new Peao ( 4 ); Horrores *ho = new Horrores( pp ); delete ho;// ho enviado ao espaço delete pp;// pp enviado ao espaço return 0; }
Um dos tópicos mais complicados em C++, logo abaixo de templates IMHO, é polimorfismo. Não estou nem mencionando, ponteiros de ponteiros e herança múltipla heheheh. Mas é comum os teóricos transformarem o assunto em algo indigesto e assim não percebemos a capacidade desse recurso dentro de uma linguagem OOP.
Polimorfismo consiste basicamente em deixar o compilador decidir qual o tipo de objeto ele está lidando. Assim, define-se uma classe base, e as classes que são herdadas e implementam os métodos abstratos da classe base.
Se você está lembrado, eu implementei uma classe que calcula a série de taylor da função seno usando herança. Só que você pode expandir a estrutura de classes. No livro "C++ Como Programar" dos Deitel, eles dão um exemplo de polimorfismo usando uma classe base Shape. Uma função recebe como parâmetro um objeto shape e decide polimorficamente qual é o tipo de objeto passado à função. Eu simplesmente, chamo a classe base de func() e a passo para um método Polimorfismo.
No entanto, você pode se perguntar pra quê? Imagine que você tenha uma estrutura de dados com vários objetos herdados de uma classe base. No nosso caso, teríamos várias funções de Taylor. Imagine que seja preciso implementar uma função que receba como parâmetro esse objeto e chame um método desse objeto. Se você tiver por exemplo, 30 séries de taylor vai ter que implementar 30 séries? Não! Por isso, o polimorfismo é importante. Em run time, o compilador sabe qual a classe está lidando!
//####################################################### //senoclass.cpp //####################################################### //autor: Daniel V. Gomes //Descrição // Calcula o valor de x à partir da série de Taylor // inicialmente, uma função base class é criada // e a partir dela derivamos as outras classes // basta implementar a função virtual calc // da classe base func. //######################################################## #include <iostream> using std::cout; using std::endl; using std::cin; //######################################################## //Classe base //######################################################## class func { public: func( long = 2 ); ~func(); virtual float calc ( float ) {}; float getValueC(){ return value; }; long getOrderOf(){ return n; }; float getErro(){ return erro; }; protected: float value; long n; float erro; }; func::func( long N){ n = N; } func::~func(){ n = 0; } //######################################################## //######################################################## //classe herdade //######################################################## class seno : public func { friend long Fatorial( long ); public: seno( long ); float calc( float ) ; }; seno::seno( long N ): func ( N ) { cout << "Seno instanciado!" << endl; } float seno::calc ( float x ){ float x_ = 3.141*x/180; float expr1 = 0; float expr2 = 0; float expr3 = 0; float meuSeno = 0; long i; for ( i = 0 ; i < n ; i++ ) { expr1 = pow(-1,i); expr2 = pow(x_,(2*i+1)); expr3 = Fatorial(2*i+1); meuSeno = meuSeno + ((expr1*expr2)/expr3); } return meuSeno; } long Fatorial( long ); void Polimorfismo( func * ); int main(){ int n; float x; cout << "Qual a ordem da expansao: "; cin >> n; cout << "Qual o valor a ser calculado: "; cin >> x; seno * s = new seno(n); cout << "O Valor de x eh: " << s->calc(x) << endl; delete s; seno * s2 = new seno( 2 ); ; //note o uso do polimorfismo. A classe base func não possui //implementação do método calc(), assim, o compilador reconhece //em tempo real, que estamos passando um objeto seno //herdado de func Polimorfismo( s2 ); system("Pause"); return 0; } void Polimorfismo ( func * f ) { cout << "Polimorfismo." << endl; cout << f->calc(2) << endl; } long Fatorial( long k ) { long n; for( n = k ; n != 1 ; n-- ) { k = k*(n-1); } return k; } //########################################################
Um dos assuntos mais complexos dentro de OOP e principalmente em OOP em C++ é herança e polimorfismo. Embora sejam nomes complicados, entendê-los demanda apenas atenção. Também considero a maneira dentro da sintaxe C++ de codificar herança, um pouco complicada comparada ao Java.
Suponha que exista uma classe base Warrior. Dessa classe derivamos uma outra classe Soldier. Nessa classe implementamos o método abstrato getTypeOfFight e sobrecarregamos Nonoverloaded.
#include <iostream> using std::cout; using std::endl; #include <string> using std::string; //********************************************************* //classe base //********************************************************* class Warrior { public: Warrior( int * , int * , int *); ~Warrior(); void StartFight(); virtual string getTypeOfFight(){}; virtual void Nonoverloaded(){ cout << "basic method!" << endl ;}; private: int size; int height; int force; }; Warrior::Warrior( int * s , int * h , int * f){ size = *s; height = *h; force = *f; } Warrior::~Warrior(){ size = 0; height = 0; force = 0; } void Warrior::StartFight(){ cout << "Start Fighting" << endl; } //************************************************ //classe derivada //************************************************ class Soldier : public Warrior { public: Soldier( string , int *s , int *h , int *f ); string getTypeOfFight(); void StopFighting() const { cout << "Fight's stopped..." << endl; }; void Nonoverloaded() { cout << "this was overloaded" << endl; }; private: string wheapon; }; Soldier::Soldier( string w , int * s , int * h , int * f):Warrior( s , h , f) { wheapon = w; } //implementa função virtual string Soldier::getTypeOfFight(){ return wheapon; }
Note que há duas funções virtuais, uma delas abstrata.
virtual string getTypeOfFight(){}; virtual void Nonoverloaded(){ cout << "basic method!" << endl ;};
O método Nonoverloaded possui o retorno padrão acima. Porém quando derivamos essa classe, sobrecarregamos esse método. O compilador sabe então que deve usar o método Nonoverloaded de Soldier e não de warrior.
Veja o programa principal:
int main() { int size = 3; int height = 4; int force = 10; Warrior *w1 = new Warrior( &size , &height , &force); Soldier *s1 = new Soldier( "lasergun" , &size , &height , &force); s1->StartFight(); cout << "Type of gun: " << s1->getTypeOfFight() << endl; s1->Nonoverloaded(); s1->StopFighting(); cout << "base class simply presented" << endl; w1->Nonoverloaded(); delete s1; delete w1; system("Pause"); return 0; }
A saída do programa é:
Start Fighting Type of gun: lasergun this was overloaded Fight's stopped... base class simply presented basic method! Press any key to continue . . .
Eu tinha implementado esse algoritmo em Pascal, mas fiquei devendo na sintaxe Java/C/C++. Ai está! Esse algoritmo é muito importante em computação numérica e ajuda que é uma barbaridade.
//************************************************************ //Implementação do método de resolução de sistemas lineares //método de Gauss //************************************************************ //Daniel Vasconcelos Gomes //************************************************************ #include <iostream> using std::cout; using std::cin; using std::endl; class Gauss { public: Gauss( float A[100][100] , int , float B[100] ); ~Gauss(); void Calcula(); float x[100]; private: int n; float a[100][100]; float *b; }; Gauss::Gauss( float A[100][100], int N , float B[100] ){ n = N; for( int i = 1 ; i <= n ; i++){ for( int j = 1 ; j <= n ; j++){ a[i][j] = A[i][j]; } }; b = B; } Gauss::~Gauss(){ b = 0; } void Gauss::Calcula(){ int k , i , j; float mik; double sum; for( k = 1 ; k <= (n - 1) ; k++ ) { for ( i = (k + 1) ; i <= n ; i++ ) { mik = a[i][k]/a[k][k]; for ( j = (k + 1) ; j <= n ; j++ ){ a[i][j]= a[i][j] - mik*a[k][j]; cout << "k= " << k << endl; }; b[i]=b[i]-mik*b[k]; } }; x[n] = b[n]/a[n][n]; for ( i = (n - 1) ; i >= 1 ; i-- ) { sum = 0; for ( j = (i + 1) ; j <= n ; j++ ) { sum = sum + a[i][j]*x[j]; }; x[i] = (b[i] - sum)/a[i][i]; }; } int main(){ int n , i , j ; float a[100][100]; float b[100]; cout << "Digite a ordem do sistema: " ; cin >> n; for ( i = 1 ; i <= n ; i++ ){ for ( j = 1 ; j <= n ; j++ ) { cout << "Digite o valor de A[" << i << "," << j << "]="; cin >> a[i][j]; } }; for ( i = 1 ; i <= n ; i++ ) { cout << "Digite o valor de B[" << i << "]="; cin >> b[i]; }; Gauss *mg = new Gauss( a , n , b ); mg->Calcula(); for( i = 1 ; i <= n ; i++){ cout << "x[" << i << "]= " << mg->x[i] << endl; }; delete mg; system("Pause"); return 0; }
Anterior Uma das grandes vantagens da orientação a objeto é poder reaproveitar código. Em uma lista de C++, um participante enviou uma questão de como implementar uma série de Taylor para sen(x) em C++. Inicialmente, enviei o código sem OOP, com uma função para calcular Fatorial e o programa principal. Depois, notei que seria interessante criar uma classe base func e a partir dela derivar as classes para cada expansão, assim, temos apenas que implementar a expansão.
//####################################################### //senoclass.cpp //####################################################### //autor: Daniel V. Gomes //Descrição // Calcula o valor de x à partir da série de Taylor // inicialmente, uma função base class é criada // e a partir dela derivamos as outras classes // basta implementar a função virtual calc // da classe base func. //######################################################## #include <iostream> using std::cout; using std::endl; using std::cin; //######################################################## //Classe base //######################################################## class func { public: func( long = 2 ); ~func(); virtual float calc ( float ) {}; float getValueC(){ return value; }; long getOrderOf(){ return n; }; float getErro(){ return erro; }; protected: float value; long n; float erro; }; func::func( long N){ n = N; } func::~func(){ n = 0; } //######################################################## //######################################################## //classe herdade //######################################################## class seno : public func { friend long Fatorial( long ); public: seno( long ); float calc( float ) ; }; seno::seno( long N ): func ( N ) { cout << "Seno instanciado!" << endl; } float seno::calc ( float x ){ float x_ = 3.141*x/180; float expr1 = 0; float expr2 = 0; float expr3 = 0; float meuSeno = 0; long i; for ( i = 0 ; i < n ; i++ ) { expr1 = pow(-1,i); expr2 = pow(x_,(2*i+1)); expr3 = Fatorial(2*i+1); meuSeno = meuSeno + ((expr1*expr2)/expr3); } return meuSeno; } long Fatorial( long ); int main(){ int n; float x; cout << "Qual a ordem da expansao: "; cin >> n; cout << "Qual o valor a ser calculado: "; cin >> x; seno * s = new seno(n); cout << "O Valor de x eh: " << s->calc(x) << endl; delete s; system("Pause"); return 0; } long Fatorial( long k ) { long n; for( n = k ; n != 1 ; n-- ) { k = k*(n-1); } return k; } //########################################################
Andei lendo sobre o assunto e iniciei a implementar uma classe que gera páginas HTML automaticamente. Como eu não perco tempo, o código é esse por enquanto!
#include <iostream> using std::cout; #include <string> using std::string; #include <cstdlib> //*************************************************************** //HEADER DA CLASSE CPPCGI //CRIA PÁGINA AUTOMATICAMENTE //AUTOR DANIEL V. GOMES //http://www.tecnologiaeoutros.hpg.ig.com.br //dansovg@ig.com.br //*************************************************************** class CppCGI { public: CppCGI( string = "" , string = "" );//construtor ~CppCGI(); void ConstruirTopoHTML() const;//constrói topo da pagina HMTL void ConstruirBaixoHTML() const;//constroi parte de baixo da pagina HTML string getTitulo(){ return titulo; }; string getTituloh(){ return tituloh;}; protected: string titulo;//titulo da pagina string tituloh;//titulo a aparecer entre os tags <h1> }; //*************************************************************** CppCGI::CppCGI( string tit , string tith ){ cout << "Content-Type: text/html\n\n"; titulo = tit; tituloh = tith; } CppCGI::~CppCGI(){} void CppCGI::ConstruirTopoHTML() const{ cout <<"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"" << "\"http://www.w3.org/TR/html4/loose.dtd\">"; cout << "<html><head>"; cout << "<link rel=\"stylesheet\" type=\"text/css\" href=\"estilos.css\"/>"; cout << "<title>" + titulo + "</title>" << "</head>"; cout << "<body>"; } void CppCGI::ConstruirBaixoHTML() const { cout << "</body>" << "</html>"; } int main() { CppCGI * html = new CppCGI ( "Pagina EM CGI", "CGI e C++"); html->ConstruirTopoHTML(); cout<< "<p> Deu certo? </p>"; html->ConstruirBaixoHTML(); delete html; return 0; }
Problema: Por ex, eu posso declarar uma array 1d long jaca[100]; e um ponteiro long * jacaptr; e fazer jacaptr = jaca Assim, posso usar o ponteiro como um array. Mas e em 2 D?
1a. opção (óbvia): usar dois indexadores (note, não é ponteiro). Ex.:
value = jaca [ i, j ]; (como nos velhos tempos do pascal, é a forma que
vejo todos usando aqui)
2a. opção (usando apenas um ponteiro):
Considere uma matriz de tamanho fixo jaca [x, y]
Defina as dimensões x e y de jaca, ex:
define x 100
define y 100
E inicialize o ponteiro: p = jaca[0, 0] e imagine que na verdade esta matriz é uma área contínua de memória. Se isso for verdade, se p+100 é igual a jaca[0,100], então jaca[1,0] é igual a p+101.
Assim basta achar uma relação entre os indexadores i, j e o ponteiro p (como fiz acima). Embora esta última solução pareça muito mais elegante, eu não aconselho, pois é um código sem garantia de portabilidade pois se for rodar para outra plataforma (outro processador) que seja enjoada, pode acontecer desta matriz não ser uma área contígua de memória e então você não conseguirá a "facilidade" descrita acima.
Resumindo, nem sempre o que é melhor para os olhos de quem cria código é o melhor na prática. Tudo depende do seu "domínio de plataforma". Se estiver absolutamente certo que nunca o código vai rodar em outras plataformas, você tem liberdade, do contrário não.
Se quiser entender mais as implicações disto pesquise na net por "memory alignment" e "portability".
Rodrigo é Engenheiro de Desenvolvimento Eletrônico da SMAR. Ele é formado na USP de São Carlos e possui 5 anos de experiência em desenvolvimento de firmware (em C++ e C) e hardware.
Vamos baixar o nível!
Vamos fugir um pouco do assunto OOP em C++ e nos dedicarmos a um assunto que incomoda muita gente (inclusive eu mesmo) que são "ponteiros" e vetores (arrays). Em Assembler é natural pensar em ponteiros, posição de memória, mas em C/C++ ficamos confusos porque a linguagem acessa diretamente a região de memória. Sabemos que não é simples fazer operações com ponteiros em Assembler. Por exemplo, imagine que seu firmware (e estou supondo que nesse caso você está programando em Linguagem de máquina) lê um valor em uma porta serial e precisa efetuar um cálculo utilizando uma função discretizada e colocada em uma área de memória específica. Imagine a complexidade para fazer estas operações.
É por isso que linguagens de alto nível auxiliam e facilitam o trabalho do programador. No caso acima, bastaria criar um vetor e colocar os dados dessa função, mas com isso perderemos em eficiência e velocidade porque o compilador faz exatamente a mesma coisa: aloca os valores na memória e utiliza um ponteiro para fazer as operações. Como referências, utilizo CplusPlus.com (onde há um excelente tutorial de C++) e o livro C++ Como Programar 3.a Edição dos irmãos Deitel.
Abaixo, exemplifico o uso de ponteiros com vetores.
// programa que exemplifica o uso de ponteiros #include <iostream> using std::cout; using std::endl; using std::cin; int main() { long A[3]; long * Aptr; Aptr = A ;// Ponteiro aponta para o vetor A // O ponteiro na verdade aponta para o inicio de A *Aptr = 1;//Inicializando a o Vetor Aptr++; *Aptr = 2;// A = { 1 , 2, null } *(Aptr + 1) = 3;//A = { 1 , 2, 3 } Aptr++; cout << "Aprt aponta para: " << *Aptr << endl << endl ; Aptr = A;// Deve apontar para A[0] cout << "Aprt aponta para: " << *Aptr << endl << endl ; for(int i = 0 ; i < 3 ; i++){ cout << "O VALOR DA ARRAY E: " << A[i] << endl ; cout << "O ENDERECO DA ARRAY E: " << &A[i] << endl; cout << "O VALOR DO PONTEIRO E: " << *Aptr << endl; cout << "O ENDERECO DO PONTEIRO E: " << &Aptr << endl; cout << endl; Aptr++; }; Aptr = A ; // Nós sabemos que a área de memória a ser coberta // por este loop corresponde à Matriz A for(int i = 0 ; i < 3 ; i++){ cout << *Aptr << endl; Aptr++; }; system("Pause"); return 0; }
Mas você deve estar se perguntando sobre rapidez e eficiência. Eu também. Para isso, fiz este programinha simples que utiliza a biblioteca <ctime> e sua respectiva classe clock_t para calcular os tempos decorridos.
// programa para avaliar a eficiencia de ponteiros em vetores #include <iostream> using std::cout; using std::endl; using std::cin; #include <ctime> int main() { long A[1000]; long *Aptr; int n; Aptr = A; cout << "DIGITE A DIMENSAO DO VETOR: " ; cin >> n ; cout << endl; //inicializa a matriz for(int i = 0 ; i < n ; i++){ *Aptr = (long)i + 50 ; Aptr++; }; Aptr = A; //ponteiros cout << "[ARRAYS UTILIZANDO PONTEIROS]" << endl; clock_t startTime1 = clock(); for(int i = 0 ; i < n ; i++){ cout << *Aptr << endl; Aptr++; }; clock_t endTime1 = clock(); double elapsedTime1 = ((endTime1 - startTime1)/static_cast<double>(CLOCKS_PER_SEC)); //Arrays cout << "[ARRAYS INDEXADAS]" << endl; clock_t startTime2 = clock(); for(int i = 0 ; i < n ; i++){ cout << A[i]<< endl; }; clock_t endTime2 = clock(); double elapsedTime2 = ((endTime2 - startTime2)/static_cast<double>(CLOCKS_PER_SEC)); cout << "Ponteiros: " << elapsedTime1 << " segundos." << endl; cout << "Indexada: " << elapsedTime2 << " segundos." << endl; system("Pause"); return 0; }
Quando escolhemos vetores de dimensão zero a 156 elementos, a eficiência dos ponteiros é verificada. Acima desse valor, utilizar vetores é mais eficiente. Por quê? Não me pergunte... :-) Assim, se o conjunto de dados é muito grande, use Arrays. Na verdade, use a STL Vector.
Abaixo um exercício muito comum. Passar parâmetros para funções por valor, usando o operador de referência &, e utilizando ponteiros. Note que nas duas últimas abordagens, o valor da variável original é alterado. Isso acontece porque o operador & cria um alias para a variável. Mudando o seu valor muda o valor original. Com ponteiros, criamos um ponteiro que aponta para a variável. Assim, alterações na posição de memória alteram a variável original.
Imagine que você passe dezenas de parâmetros para funções em seu programa. Se você utilizar a passagem por valor, poderá estourar o heap (num caso extremo). Utilizando as duas outras abordagens, a memória ocupada é bem menor. Utilizando o operador & temos maior segurança.
#include <iostream> using std::cout; using std::endl; using std::cin; int PassadoPorValor( int ); int PassadoPorReferencia ( int & ); int PassadoPorReferenciap ( int * ); int main() { int a; int b; int c; a = 5; b = 5; c = 5; cout << "ANTES a: " << a << endl; cout << "ANTES b: " << b << endl; cout << "ANTES c: " << c << endl; cout << "PASSADO POR VALOR: " << PassadoPorValor( a ) << endl; cout << "PASSADO POR REFERENCIA: " << PassadoPorReferencia( b ) << endl; cout << "PASSADO POR REFERENCIA(p): " << PassadoPorReferenciap( &c ) << endl; cout << "DEPOIS a: " << a << endl; cout << "DEPOIS b: " << b << endl; cout << "DEPOIS c: " << c << endl; system("Pause"); return 0; } int PassadoPorValor( int val ){ val = val + 1; return val; } int PassadoPorReferencia( int & val ){ val = val + 1; return val; } int PassadoPorReferenciap( int *val ){ *val = *val + 1; return *val; }
A saída do programa acima é:
Vamos passar um vetor para uma função utilizando ponteiros...
#include <iostream> using std::cout; using std::endl; using std::cin; void PassarVetor ( int * , int ); int main(){ int A[10]; int B[10][10]; int i; for( i = 0 ; i < 10 ; i++) A[i] = i + 20*i ; PassarVetor( A , 10 ); system("Pause"); return 0; } void PassarVetor ( int * Vect , int n ){ int i; cout << "O VETOR FOI PASSADO POR REFERENCIA" << endl; for( i = 0 ; i < n ; i++){ cout << *Vect << endl; Vect++; } }
A saída do programa acima é:
Acho que por enquanto está bom de ponteiros!
Imagine que um programa leia uma pressão e uma temperatura. Tentei fazer isso, inclusive utilizando um atraso que simula um tempo de leitura. O programa atribui o tempo de atraso através da prioridade. Na verdade, isso tudo foi para motivar o estudo de funções virtuais, herança e polimorfismo. Note, que existe uma classe base e duas classes derivadas. E ambas possuem uma função ExecuteThread(). O restante é comum para ambas as classes.
//##################################################### //referencias //##################################################### #include <iostream> using std::cout; using std::cin; using std::endl; #include <time.h> #include <ctime> //##################################################### //Classe base //Prioridades: // 0 baixa // 1 média // 2 alta //##################################################### class _ThreadIng_ { friend void time_delay( int ); public: _ThreadIng_( int ); ~_ThreadIng_(); void startThread_(){start = true;}; void stopThread_(){start= false;}; virtual void ExecuteThread(){} ; int getTempo(){return tempo;}; bool start; protected: int prior; int tempo; }; _ThreadIng_ ::_ThreadIng_ ( int priority ){ prior = priority; if (prior==0) tempo= 1;//1 s else if (prior==1) tempo= 2;//2 s else tempo = 0;//default start = false; } _ThreadIng_ ::~_ThreadIng_ (){ start = false; prior = 0; } //##################################################### //Classe herdada implementa a Thread 1 //A Thread 1 lê um sensor de temperatura //##################################################### class Thread1 : public _ThreadIng_ { public: Thread1( int ); void ExecuteThread(); }; Thread1::Thread1( int priority ): _ThreadIng_( priority ){ cout << "Thread 1 created!" << endl; } void Thread1::ExecuteThread(){ cout << "Just read the temperature value!" << endl; time_delay( this->tempo); } //##################################################### //Classe herdada implementa a Thread 2 //A Thread 1 lê um sensor de pressão //##################################################### class Thread2 : public _ThreadIng_ { public: Thread2( int ); void ExecuteThread(); }; Thread2::Thread2( int priority ): _ThreadIng_( priority ){ cout << "Thread 2 created!" << endl; } void Thread2::ExecuteThread(){ cout << "Just read the pressure!" << endl; time_delay( this->tempo); } //##################################################### //Programa principal //##################################################### void time_delay( int ); int main() { int cont ; int i; Thread1 *t1 = new Thread1( 0 ); Thread2 *t2 = new Thread2( 1 ); t1->startThread_(); t2->startThread_(); clock_t startTime = clock(); for ( i = 0 ; i < 3 ; i++){ t1->ExecuteThread(); t2->ExecuteThread(); }; clock_t endTime = clock(); double elapsedTime = ((endTime - startTime)/static_cast<double>(CLOCKS_PER_SEC)); cout << "It took " << elapsedTime << " seconds" << endl; delete t1; delete t2; system("Pause"); return 0; } /* De Visual C++ 6 Complete Reference Chris Pappas/William Murray página 517 */ void time_delay( int t) { long initial,final; long ltime; initial = time(<ime); final = initial + t; while (time(<ime) < final); return; }
Considere um tanque preenchido com um líquido cuja altura é Hs e cuja densidade é dp. de é a densidade do líquido de preenchimento da tubulação onde está o transmissor. A diferença de pressão medida no transmissor é calculada. É se que por enquanto está meia boca, mas fazer quê?
/* /////////////////////////////////////////////////////////////////////////////// //Programa para calcular a pressão de um tanque utilizando um transmissor //de pressão DT301 /////////////////////////////////////////////////////////////////////////////// //Desenvolvimento teórico: José Roberto Nogueira/Diego Foltz Hanser //Desenvolvimento matemático/algoritmo e programação: Daniel Vasconcelos Gomes /////////////////////////////////////////////////////////////////////////////// //AP(HV,HE) = HV*DE-HS*DP //Onde: // HV= altura do tanque // HS= altura da coluna de líquido // de densidade do líquido // dp densidade do produto contido no tanque // AP: variação da temperatura /////////////////////////////////////////////////////////////////////////////// */ #include <iostream> using std::cout; using std::cin; using std::endl; class PressaoDif { friend double FunctionValue( double *, double *, double *, double *); public: PressaoDif( int * = 0 ); ~PressaoDif(); void calcPressao(); double getde(){return de;}; double getdp(){return dp;}; double geths(){return hs;}; double gethv(){return hv;}; double getpfinal(){return pfinal;}; int option; protected: double de, dp, hs, hv, pfinal; }; PressaoDif::PressaoDif( int * opt ){ option = *opt; pfinal = 0; de = 0; dp = 0; hs = 0; hv = 0; } PressaoDif::~PressaoDif(){ option = 0; } void PressaoDif::calcPressao(){ switch(option){ case 0: //AP=0 cout << "DIGITE O VALOR DE DE (g/cm3):" << endl; cin >> de; cout << "DIGITE O VALOR DE DP (g/cm3):" << endl; cin >> dp; if (de>dp) { cout << "DIGITE O VALOR DE HS(cm):" << endl; cin >> hs; hv = (de/dp)*hs; } else { cout << "NAO PODE SER CALCULADO!" << endl; cout << "DE DEVE SER MAIOR DO QUE DP!" << endl; }; break; case 1: //AP<0 cout << "DIGITE O VALOR DE DE(g/cm3):" << endl; cin >> de; cout << "DIGITE O VALOR DE DP(g/cm3):" << endl; cin >> dp; if (dp<de) { cout << "NÃO PODE SER CALCULADO!" << endl; cout << "dp DEVE SER MAIOR DO QUE de!" << endl; break; } else { cout << "DIGITE O VALOR DE HS(cm):" << endl; cin >> hs; cout << "DIGITE O VALOR DE HV(cm):" << endl; cin >> hv; while (((hv/hs)<1)&&((hv/hs)>(dp/de))) { cout << "ERRO!" << endl; cout << "DIGITE O VALOR DE HS(cm):" << endl; cin >> hs; cout << "DIGITE O VALOR DE HV(cm):" << endl; cin >> hv; }; pfinal = FunctionValue( &hv , &hs , &de , &dp ); }; break; case 2: //AP>0 cout << "DIGITE O VALOR DE DE(g/cm3):" << endl; cin >> de; cout << "DIGITE O VALOR DE DP(g/cm3):" << endl; cin >> dp; if ((dp/de)<1) { cout << "DIGITE O VALOR DE HS(cm):" << endl; cin >> hs; cout << "DIGITE O VALOR DE HV(cm):" << endl; cin >> hv; while ((hv/hs)<1) { cout << "ERRO!" << endl; cout << "DIGITE O VALOR DE HS(cm):" << endl; cin >> hs; cout << "DIGITE O VALOR DE HV(cm):" << endl; cin >> hv; }; pfinal = FunctionValue( &hv , &hs , &de , &dp ); break; } else { cout << "DIGITE O VALOR DE HS(cm):" << endl; cin >> hs; cout << "DIGITE O VALOR DE HV(cm):" << endl; cin >> hv; while ((hv/hs)<(dp/de)) { cout << "ERRO!" << endl; cout << "DIGITE O VALOR DE HS(cm):" << endl; cin >> hs; cout << "DIGITE O VALOR DE HV(cm):" << endl; cin >> hv; }; pfinal = FunctionValue( &hv , &hs , &de , &dp ); break; }; break; } } double FunctionValue( double *, double *, double *, double *); int main(){ int op; double ps; int cprog; cprog= 0; while (cprog == 0){ cout << "OPCOES" << endl; cout << "*****************************" << endl; cout << "OPCAO (0) AP = 0 " << endl; cout << "OPCAO (1) AP < 0 " << endl; cout << "OPCAO (2) AP > 0 " << endl; cout << "*****************************" << endl; cout << "DIGITE A OPCAO!: " << endl; cin >> op; PressaoDif *p = new PressaoDif ( &op ); p->calcPressao(); ps = p->getpfinal(); if ( ps!= 0 ) { cout << "O VALOR DA PRESSAO (g/cm2): " << p->getpfinal() << endl; } else { cout << "HV PARA AP=0: " << p->gethv() << endl; }; delete p; cout << "DESEJA CONTINUAR ? (SIM:0)(NAO:qualquer outra tecla)-"; cin >> cprog; }; system("Pause"); return 0; } double FunctionValue( double * HV, double * HS, double * DE, double * DP){ return (*HV)*(*DE) - (*HS)*(*DP); }
Mais outro programa.
#include <iostream> using std::cout; using std::endl; using std::cin; #include <cmath> class frequencias { public: frequencias( int ); ~frequencias(); double getA(); double getB(); double getC(); double getpe(); void calcfreq(); protected: double a, b, c, pe; double V[100]; int N; }; frequencias::frequencias( int n ){ N = n; a = 1; b = 0; c = 1; for( int i = 0; i < N ; i++){ cout << "DIGITE A FREQUENCIA pi" << i << endl; cin >> V[i]; }; } frequencias::~frequencias(){ N = 0; a = 0; b = 0; c = 0; pe = 0; } double frequencias::getA(){return a;} double frequencias::getB(){return b;} double frequencias::getC(){return c;} double frequencias::getpe(){return pe;} void frequencias::calcfreq(){ int i, j; for( i = 0; i < N ; i++ ) for( j = i; j < N ; j++ ) a = ((4-3*V[i]-3*V[j])*a); for( i = 0; i < N ; i++) c = (c*pow(V[i],2)); for( i = 0; i < N ; i++) b = (b + V[i]*pow(1-V[i],2)); pe = (b-(0.5*c*a)); } int main(){ int n; cout << "DIGITE O NÚMERO DE FREQUENCIAS- " << endl; cin >> n; frequencias *f = new frequencias( n ); f->calcfreq(); cout << "O VALOR DE A: " << f->getA() << endl; cout << "O VALOR DE B: " << f->getB() << endl; cout << "O VALOR DE C: " << f->getC() << endl; cout << "O VALOR DE pe: " << f->getpe() << endl; delete f; system("pause"); return 0; }
Eu lembro de ter feito isso para o meu amigo Rog, mas nem lembro direito pra que serve. Converti para C++...
#include <iostream> using std::cout; using std::endl; using std::cin; #include <cmath> class somatorio { public: somatorio( int , int ); ~somatorio(); double pe(); protected: int N; int M; double pi[100]; double pj[100]; }; somatorio::somatorio( int n , int m){ N = n; M = m; for( int i = 0 ; i < N ; i++ ){ cout << "DIGITE A FREQUENCIA pi" << i << endl; cin >> pi[i]; }; for( int j = 0 ; j < M ; j++ ){ cout << "DIGITE A FREQUENCIA pj" << j << endl; cin >> pj[j]; }; } somatorio::~somatorio(){N=0;M=0;}; double somatorio::pe(){ int i, j; double cont; double soma; double pe; cont = 0; for( i = 0 ; i < N ; i++) for( j = 0 ; j < M ; j++) cont = (pow(pi[i],2)*pow(pj[j],2)*(4-3*pi[i]-3*pj[j])+cont); soma=cont/2; cont = 0; for( i = 0 ; i < N ; i++) cont = pi[i]*pow(1-pi[i],2)+ cont ; pe = cont + soma; return pe; } int main(){ int n; int m; cout << "QUANTOS ALELOS TIPO N:- "; cin >> n ; cout << "QUANTOS ALELOS TIPO M:- "; cin >> m ; //aloca memória para o objeto s //o detalhe: o construtor vai alocar memória para //dois vetores, e ao final, esta memória é liberada somatorio * s = new somatorio( n , m ); cout << "O VALOR Pfinal: " << s->pe() << endl; system ("pause"); delete s; return 0; }
O projeto é distribuído entre duas classes:
-CComplex
-Transforms
Estou usando a versão Introdutória do Visual C++ 6.0. Ainda não entendi como
o compilador faz a conexão entre essas duas classes e o programa principal. Por
isso coloquei todas as classes em apenas um diretório.Veja o
Código.
Ainda há alguns detalhes a acertar: as matrizes têm que ser manualmente dimensionadas e com isso a memória alocada para o código estoura o HEAP. Eu dei uma ajeitada para otimizar o código, veja abaixo.
Estou estudando maneiras de alocar dinamicamente a memória necessária. A lógica de ponteiros é ainda, pra mim, um pouco nebulosa para Arrays 2D. Estou pensando em utilizar a estrutura Vector. Da maneira como está, consegui que as matrizes tenham no máximo 100x100 pixels. Uma idéia é particionar a imagem original e aplicar a transformada por partes. Maiores detalhes veja este link.
O grande mestre do C++, criador da linguagem, Bjarne Stroustrup, aconselha a não usarmos macros. Eu tentei fazer isso neste código! Infelizmente, essa desgraça não converge. Deve ser o EXP(número complexo). Preciso arrumar isso.
///////////////////////////////////////// //Cálculo da Transformada 2D de Fourier //Imagem(x,y) NxN //////////////////////////////////////// //Referência: Gonzales/Woods //Processamento de Imagens Digitais //////////////////////////////////////// #include <iostream> using std::cout; using std::endl; using namespace std; #include <exception> using std::exception; #include <stdexcept> using std::runtime_error; #include <complex> #include <cmath> #include <vector> typedef unsigned int uint; //////////////////////////////////////// //Header da classe //////////////////////////////////////// class Fourier2D { public: Fourier2D( uint = 0 ); ~Fourier2D(); void Trans2D( uint Image[100][100] ); uint ImageT[100][100]; private: complex <double> Fxv[100][100]; uint n; }; //////////////////////////////////////// //Implementação /////////////////////////////////////// //Construtor Fourier2D::Fourier2D( uint N){ n = N; } //destrutor Fourier2D::~Fourier2D(){} //Função membro void Fourier2D::Trans2D( uint Image[100][100] ){ const double PI = 3.1415926535897932384626433832795; // calculo de F(x,v) complex <double> A ( 0 , 0 ); for( uint x = 0 ; x < n ; x++ ){ for( uint v = 0 ; v < n ; v++ ){ for(uint y = 0 ; y < n ; y++){ complex <double> c1 ( 0 , ((-n*2*PI*v*y)/n) ); complex <double> c2 = exp(c1); A = A + ((complex <double>)Image[x][y])*c2; }; Fxv[x][v] = A; A = ( 0 , 0 ); } }; //calculo de F(u,v) for( uint u = 0 ; u < n ; u++ ){ for( uint v = 0 ; v < n ; v++ ){ for(uint x = 0 ; x < n ; x++){ complex <double> c1 = ( 0 , ((-n*2*PI*v*x)/n) ); complex <double> c2 = exp(c1); A = A + Fxv[x][v]*c2; }; ImageT[u][v] = (int)(1/n)*abs(A); A = ( 0 , 0 ); } }; } //Fim da Implementação da Classe int main() { uint dados[100][100]; for( uint i = 0; i < 10 ; i++){ dados[i][i]= i; }; Fourier2D f(10); f.Trans2D( dados ); for( uint j = 0; j < 10 ; j++){ i = 0; while (i < 10) { cout << (f.ImageT[j][i]/10000) << " " ; ++i; } cout << endl; }; return 0; }
///////////////////////////////////// //Classe para gerar polinômios ///////////////////////////////////// #ifndef POLINOMIO_H #define POLINOMIO_H #include <iostream> using std::cout; using std::endl; class Polinomio { public: Polinomio( int = 0 , double * = 0); ~Polinomio(); double getValueOfX(double x); private: int n; double * coefptr; }; #endif ///////////////////////////////////// //Implementação da classe polinômios ///////////////////////////////////// #include <iostream> using std::cout; using std::endl; #include <cmath> #include "Polinomios.h" Polinomio::Polinomio(int _n, double coef[]){ n = _n; coefptr = coef; } double Polinomio::getValueOfX(double x){ int i; double aux; double resultado; resultado = 0; for(i=0; i < n; i++){ aux = pow(x,i)*coefptr[i]; resultado = aux+resultado; }; return resultado; }
É um exercício proposto no livro C++ Como Programar dos irmãos Deitel. O próximo passo é alocar os objetos dinamicamente.
São três classes: Ponto, Quadrado e Cubo. E ao final, o programa teste.
////////////////////////////////////////////// //Header da classe Ponto ///////////////////////////////////////////// #ifndef PONTO2_H #define PONTO2_H #include <iostream> using std::ostream; class Ponto { // Sobrecarga do operador << para imprimir o ponto // ostream e o objeto Ponto são passados por referência friend ostream &operator<<( ostream &, const Ponto &); public: //contrutor Ponto (double = 0, double = 0); void setPonto( double, double); double getX() ; double getY() ; protected: double x,y; }; #endif ///////////////////////////////////////////// //Implementação da classe ponto ///////////////////////////////////////////// #include "ponto.h" //Implementação do construtor Ponto::Ponto(double a, double b) { setPonto(a,b); } //Inicialização do Ponto void Ponto::setPonto(double a, double b) { x = a; y = b; } double Ponto::getX() { return x; } double Ponto::getY() { return y; } //Sobrecarga do operador << ostream &operator<<(ostream &output, const Ponto &p) { output << '[' << p.x << p.y << ']'; return output; } ///////////////////////////////////////////////// ////////////////////////////////////////////// //Header da classe Quadrado ///////////////////////////////////////////// #ifndef QUADRADO_H #define QUADRADO_H #includeusing std::ostream; #include "ponto.h" class Quadrado : public Ponto { friend ostream &operator<<( ostream &, const Quadrado& ); public: Quadrado( double lado = 0.0, double x = 0, double y = 0 ); void setLado( double ); double getLado() const; double area() const; protected: double lado; }; #endif //////////////////////////////////////////////// ///////////////////////////////////////////// //Implementação da classe Quadrado ///////////////////////////////////////////// #include using std::ios; using std::setiosflags; using std::setprecision; #include "Quadrado.h" //Implementação do construtor Quadrado::Quadrado( double lado, double a, double b): Ponto( a , b ){ setLado( lado ); } //Inicializa lado do quadrado void Quadrado::setLado( double lado){ lado = ( lado<0 ? lado : 0); } //obtem o lado double Quadrado::getLado() const{ return lado; } //área do quadrado double Quadrado::area() const{ return lado*lado; } //sobrecargada do operador << ostream &operator<<( ostream &output, const Quadrado & q ) { output << "Centro= " << static_cast< Ponto > (q) << ";Lado = " << setiosflags ( ios::fixed | ios::showpoint ) << setprecision ( 2 ) << q.lado; return output; } ///////////////////////////////////////////////////////// ////////////////////////////////////////////// //Header da classe Cubo ///////////////////////////////////////////// #ifndef CUBO_H #define CUBO_H #include using std::ostream; #include "Quadrado.h" class Cubo : public Quadrado { public: //construtor Cubo( double h = 0.0 , double lado = 0.0, double x = 0, double y = 0 ); void setAltura( double ); double getAltura() const; double area() const; double volume() const; protected: double altura; }; #endif ////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////// //Implementação da classe Cubo ///////////////////////////////////////////// #include "Cubo.h" Cubo::Cubo(double h, double lado, double x, double y) : Quadrado( lado , x, y ) { setAltura( h ); } void Cubo::setAltura( double h) { altura = ( h >= 0 ? h : 0 ); } double Cubo::getAltura() const { return altura; } double Cubo::area() const { return 2*lado*lado + 4*altura*altura; } double Cubo::volume() const { return Quadrado::area()*altura; } ///////////////////////////////////////////////// ///////////////////////////////////// //Utilização das classes ///////////////////////////////////// #include using std::cout; using std::endl; #include #include "ponto.h" #include "Cubo.h" #include "Quadrado.h" int main() { Ponto p1(1,1); Quadrado q1(1,1,1); Cubo c1(1,1,1,1); Ponto p2(2,2); Quadrado q2(2,2,2); Cubo c2(2,2,2,2); //vamos calcular algumas distâncias double d1; double xdif, ydif; double x1,x2,y1,y2; x1 = (p1.getX()); x2 = (p2.getX()); y1 = (p1.getY()); y2 = (p2.getY()); xdif = x1-x2; ydif = y1-y2; d1 = sqrt(pow(xdif,2) + pow(ydif,2)); cout << d1 << endl; return 0; } //////////////////////////////////////////////////////////// // Com alocação dinâmica ////////////////////////////////////////////////////////////// ///////////////////////////////////// //Utilização das classes ///////////////////////////////////// #include using std::cout; using std::endl; #include #include "ponto.h" #include "Cubo.h" #include "Quadrado.h" int main() { Ponto *p1 = new Ponto(1,1); Quadrado *q1 = new Quadrado(1,1,1); Cubo *c1 = new Cubo(1,1,1,1); Ponto *p2 = new Ponto(2,2); Quadrado *q2 = new Quadrado(2,2,2); Cubo *c2 = new Cubo(2,2,2,2); //vamos calcular algumas distâncias double d1; double xdif, ydif; double x1,x2,y1,y2; x1 = (p1->getX()); x2 = (p2->getX()); y1 = (p1->getY()); y2 = (p2->getY()); xdif = x1-x2; ydif = y1-y2; d1 = sqrt(pow(xdif,2) + pow(ydif,2)); cout << d1 << endl; delete p1; delete q1; delete c1; delete p2; delete q2; delete c2; return 0; }