Organizacao de um aplicativo em C++ / + duvidas de iniciante

36 respostas
faeldix

Estou aprendendo c++ e queria saber como é organizado um aplicativo em C++.
ps -> não estou usando IDE, pretendo fazer esse exemplo de baixo na unha mesmo, pq assim acho que aprendo mais…

Exemplo, vou criar o exemplo clássico de uma agenda de contatos em c++.

Até onde eu entendi… os headers (.h) funcionam como uma interface (similares ao java) com a diferenca de que nos headers pode haver implementações e nao somente a assinatura do metodo (classes abstratas do java???).

Em Java nem sempre precisamos de interfaces para fazer alguma coisa, no c++ é uma boa prática ou é igual ao java, não é necessário tbm? as classes devem ser DECLARADAS em um header ou em um arquivo de implementação? (.cpp) uma arquivo por classe?

agora umas duvidas bobas de iniciante:

  • a declaracao using namespace std; é semelhante ao static import do java?

  • existe garbage collector ou algo parecido no c++?
    pq a duvida?

Quando eu crio um objeto no c++ e depois de utiliza-lo o destrutor é chamado e toda a memoria utilizada para manter informações sobre aquele objeto é liberada? Se sim, caso esse objeto tenha um atributo que é um ponteiro pra um array por exemplo, criado utilizando malloc. Quando objeto morre, aquela área reservada utilizando o malloc morre tbm, ou isso deve ser feito pelo programador??

enfim, pessoa que os moderadores tenham pena de mim, sei que o forum é sobre java, mas é tao escasso bom material aqui sobre a linguagem que tive que vir pra cá, sei que aqui tem bons programadores de c++.

thx a todos… de acordo com o tempo que vou estudando e for sugindo duvidas eu posto aqui :smiley:

EDIT:

  • A implementação dos métodos da classe devem ser feitos dentro da classe ou fora? existe uma grande diferença entre as duas formas?

36 Respostas

ViniGodoy

Não, não é isso. O C++ tem dois conceitos distintos, o de declaração e o de definição.

Declarar uma classe significa dizer ao compilador qual será a assinatura de seus métodos, e quais propriedades ela irá conter. A declaração vai no .h.
A definição é a implementação do método em si. Ela estará no arquivo .cpp, ou pode estar já compilada em alguma das libs que você incluiu no seu projeto.

Não confunda o .h com interfaces do Java, pois, embora ali não estejam escritas as definições dos métodos, elas estarão escritas em algum lugar, geralmente no arquivo .cpp correspondente.

Leia também:
http://pontov.com.br/site/cpp/46-conceitos-basicos/95-compilacao-de-programas-cc
http://pontov.com.br/site/cpp/46-conceitos-basicos/155-como-usar-bibliotecas-cc

No C++ usam-se 2 arquivos por classe. Um para a definição (.h) e outro para as declarações (.cpp).
A definição é também chamada de interface pública da classe, mas esse conceito não deve ser confundido com o de “interface” do Java. Mesmo as classes em Java tem uma interface pública, que é a que o “code completion” apresenta para você.

Uma classe não precisa necessariamente ter uma interface. Na verdade, embora seja possível criar interfaces em C++, não existe uma palavra chave para isso.

Não, é similar ao import, sem o static.
Detalhe, o #include não é similar ao import.

Não existe.
Objetos criados no stack (sem o uso de new) são automaticamente destruídos quando saem de escopo, e são automaticamente copiados ao serem passados por parâmetro.
Objetos criados no heap (com new) devem ser explicitamente deletados com delete.

Para mais informações, leia:
http://pontov.com.br/site/cpp/43-smart-pointers/52-smart-pointer-introducao
http://pontov.com.br/site/cpp/46-conceitos-basicos/335-exception-safety-e-gerencia-de-recursos

std::string, toda vida.

Sim, basta fazer:

Acredito que QT e, infelizmente, a MFC.

http://www.pontov.com.br/files/cpp/roadmapcpp/ApostilaProgramacaoCppv045.pdf

Sem problemas, até por isso temos um fórum de outras linguagens.

faeldix

por exemplo:

quando eu uso “using namespace std” não é necessario eu utilizar "#include "
???

Onde fica a implementacao de tudo isso?? dentro do compilador??
pois nao lembro de baixar um sdk nem nada

estou utilizando ubuntu + g++

EDIT:

quando eu dou um #include <algo.h> eu estou apenas importando sua ‘interface’, mas como você disse todo header tem sua implementacao, nao é possivel criar um header para ser implementado depois??

ViniGodoy

Não é possível criar um header para ser implementado depois.
O C++, assim como o Java, tem uma biblioteca padrão, chamada de STL (Standard Template Library). Ela tem um conjunto padrão de funções e classes, como as classes std::string e std::vector.

Você precisa fazer o #include <string> se quiser usar a classe String. O que esse comando faz é dizer ao seu programa para “recortar e colar” o .h da classe string no seu arquivo, no ponto onde o #include é dado.

O string está no pacote (no C++ chamado de namespace) std. std é a sigla de standard. Portanto, quando você faz using namespace std; você pode parar de usar o std:: na frente do nome da classe e escrever somente string.
Em java é muito similar. Você pode escrever o nome de uma classe de maneira completa como java.util.ArrayList ou de maneira resumida, desde que faça o import, usando somente ArrayList.

A diferença é que em java você não precisa indicar em que arquivo o import foi declarado, pois o compilador se vira para encontrar isso sozinho.

faeldix

duvida:

http://pt.wikibooks.org/wiki/Programar_em_C%2B%2B/Manipulando_strings#comparar_frases nesta parte que ele fala sobre a comparacao

ele diz que essa comparacao da como verdadeiro.. mas não dá.. ou eu que não entendi o que ele quis dizer ali?

#include <iostream>
#include <string>
#include <string.h>

using namespace std;

int main(void){

	char oi[10] = "chess";
	char o2[15] = "check";

	if(strcmp(oi, o2) == 0){
		cout << "Sao Iguais C";
	} else {
		cout << "Sao Diferentes C";
	}

	return 0;
}
J
faeldix:
duvida:

http://pt.wikibooks.org/wiki/Programar_em_C%2B%2B/Manipulando_strings#comparar_frases nesta parte que ele fala sobre a comparacao

ele diz que essa comparacao da como verdadeiro.. mas não dá.. ou eu que não entendi o que ele quis dizer ali?

#include <iostream>
#include <string>
#include <string.h>

using namespace std;

int main(void){

	char oi[10] = "chess";
	char o2[15] = "check";

	if(strcmp(oi, o2) == 0){
		cout << "Sao Iguais C";
	} else {
		cout << "Sao Diferentes C";
	}

	return 0;
}

Não é igual mesmo. Não será uma errata? E outra, porque não usar string?

#include <iostream>
#include <string>
#include <string.h>

using namespace std;

int main(void){

	string oi  = "chess";
	string o2 = "check";

	if(strcmp(oi, o2) == 0){
		cout << "Sao Iguais C";
	} else {
		cout << "Sao Diferentes C";
	}

	return 0;
}
ViniGodoy

No texto ele diz que o valor será “positivo”, e não igual a zero. Ou seja, não significa que as duas strings serão iguais.
Positivo é qualquer valor maior do que zero.

Isso significa que a palavra check deve vir antes de chess.

É que na verdade, o strcmp funciona exatamente igual ao método compareTo da classe String. E a tabela de onde você tirou o chess e o check mostra isso.

ViniGodoy

O ideal mesmo é usar a classe std::string. Ficar manipulando char* é coisa de programador C.

faeldix

There are several essential differences.

  1. In C++, there are public and private sections, started by the keywords public and private. In Java, each individual item must be tagged with public or private.
  2. The class definition only contains the declarations of the methods. The actual implementations are listed separately.
    3. Accessor methods are tagged with the keyword const
  3. There is a semicolon at the end of the class

é necessário isso mesmo??? Qual a serventia??

ViniGodoy

O C++ implementa um conceito chamado de “constness”. Quando você declara um objeto como const, não está dizendo que apenas a referência dele é const, mas sim, o objeto mesmo.

Portanto, se você fizer:

Ele não vai deixar você chamar nenhum método de abc que altere a string.

Mas, como a linguagem sabe quais métodos alteram ou não a String? Por que os métodos que não alteram o estado da classe são marcados como const.

Essa última afirmação não está 100% correta. Um getter que altere o estado da classe, embora seja exceção, não deve ser marcado como const. Na verdade, ao dizer que um método é const, o C++ irá te proibir de alterar o valor de qualquer atributo.

É assim que se declara um método const:

class Exemplo { private: int x; public: int getX() const; };

faeldix

Vini obrigado pela força cara…

Mas vamos la… mais duvidas:

Li isso na apostila que encontrei no seu site:

so nao encontrei o pq…

ViniGodoy

Por que o using irá valer para todos os arquivos que importarem aquele .h.

É importante lembrar que o #include não é igual ao import do Java (o using é que é).

#include equivale a dizer para o pré-processador pegar o conteúdo de um arquivo e colocar no ponto onde o #include foi dado. Um exemplo de uso diferente do #include é no jogo Quake. Eles colocaram um 3D Studio da vida para calcular os vetores normais dos modelos, criando 256 normais que mapeavam uma esfera. Então salvaram um arquivo nesse formato, com 256 chanves como essa:

Então, no C++ fizeram isso aqui:

double [][] anorms = { #include "anorms.txt" };

O pré-processamento ocorre antes da compilação.

Portanto um using num arquivo .h irá ser dado sobre qualquer arquivo que o incluir. O programador acaba ganhando de graça um using, o que vai tirar o sentido de usar namespaces.

faeldix

perfeito vinicius.. obrigado..

--

tentei implementar um singleton

header

class Teste {
	Teste(){}
	Teste(Teste&){}
public:
	static Teste getInstance();
	void imprime();
};

cpp

#include <iostream>
#include "teste.h"

using namespace std;

Teste Teste::getInstance(){
	static Teste t;
	return t;
}

void Teste::imprime(){
	cout << "Olá" << endl;
}

main

#include <iostream>
#include "teste.h"

using namespace std;

int main(){
	Teste::getInstance().imprime();
	return 0;	
}

TA OK?

Tentei colocar a variavel estatica na classe.. mas nao conseguia "puxar" com o '->' pra chamar a funcao imprime ;/

tipo assim:

header

class Teste {
	static Teste *p;
	Teste(){}
	Teste(Teste&){}
public:
	static Teste* getInstance();
	void imprime();
};

cpp

#include <iostream>
#include "teste.h"

using namespace std;

Teste* Teste::getInstance(){
	p = new Teste;
	return p;
}

void Teste::imprime(){
	cout << "Olá" << endl;
}

erro:

rafael@rafael-note:~/Área de Trabalho/C/C++$ g++ teste.h teste.cpp operadores.cpp /tmp/cclMyyDI.o: In function `Teste::getInstance()': teste.cpp:(.text+0x1f): undefined reference to `Teste::p' teste.cpp:(.text+0x24): undefined reference to `Teste::p' collect2: ld returned 1 exit status
ViniGodoy

Cuidado, você está retornando por valor. Isso tentará criar uma cópia de Teste.

Mude o retorno do getInstance() para Teste&. Assim você retorna só a referência, sem cópias.

ViniGodoy
Vai ficar assim:
//Teste.h
class Teste {
	Teste(){}

	Teste(const Teste&);
	Teste operator = (const Teste&);
public:
	static Teste& getInstance();
	void imprime();
};
#include &lt;iostream&gt;
#include &quot;teste.h&quot;

using namespace std;

Teste& Teste::getInstance(){
	static Teste t;
	return t;
}

void Teste::imprime(){
	cout &lt;&lt; &quot;Olá&quot; &lt;&lt; endl;
}
faeldix

e utilizando a variavel dentro da classe e não do metodo??

ViniGodoy
Basicamente a mesma coisa, mas não vejo muito por que ser desse jeito: Vai ficar assim:
//Teste.h
class Teste {
private:
	static Teste t;

	Teste(){}
	//Não é necessário fornecer implementação
	//vazia para esses 2. 
	Teste(const Teste&);
	Teste operator = (const Teste&);
public:
	static Teste& getInstance();
	void imprime();
};
#include &lt;iostream&gt;
#include &quot;teste.h&quot;

using namespace std;

Teste& Teste::getInstance(){
	return t;
}

void Teste::imprime(){
	cout &lt;&lt; &quot;Olá&quot; &lt;&lt; endl;
}
faeldix

Vinny me ajude a entender o que ele quiser dizer nessa parte…

[quote]? Se uma função espera uma referência e recebe um ponteiro ela aceita mas pode causar um bug.

Exemplo:

//Prototipo do método:

//O método espera uma referência

TBitmap(const TDC& dc, const TDib& dib);

TDib* dib;

TBitmap (dc,dib); //Uso errado, passando um ponteiro

TBitmap (dc,*dib); //uso correto, passando o objeto

? 2 BUG: Digamos que você deseja passar um objeto como parâmetro para um método

f(nomeclasse obj). Como você esta passando o objeto por cópia, vai criar uma cópia do objeto.

Depois vai usar o objeto dentro do método, e, ao encerrar o método, o objeto é deletado. Se o objeto

original tinha algum ponteiro com alocação dinâmica, este ponteiro é deletado, sendo deletado o

bloco de memória por ele acessado. Assim, o ponteiro do objeto original aponta agora para um

monte de lixo. Para que isto não ocorra você deve passar uma referência do objeto, de forma que

é passado o objeto e não uma cópia deste, quando sair de escopo o objeto não é eliminado.

[/quto]

pq tentei causar esse bug… mas nao funcionou…


  • uma duvida

construtores sao definidos no .h ou no .cpp?
variaveis estaticas sao inicizalidas no .h ou no .cpp?

ViniGodoy

Esse bug está mal colocado, meio cedo para falar desse tipo de detalhe, e ainda de forma tão superficial.
Mas existe mesmo. Considere a seguinte classe:

class Foo {
   private:
        MeuObjeto* obj;
   public:
       ...
       Foo() : obj(new MeuObjeto()) {
       }

       ~Foo() {
           delete obj;
       }
};
Agora, considere que você passa Foo por cópia, como no método abaixo:
void doSomething(Foo foo);
Veja o que acontece nesse código abaixo:
Foo foo;
doSomething(foo);
cout &lt;&lt; foo.getObj().getName(); //Crash
Essa classe não tem construtor de cópia implementado, portanto, ele vai fazer uma cópia do objeto ao entrar em doSomething, chamando a implementação padrão. Essa implementação faz a cópia do ponteiro, mas não do objeto que ele aponta (ela se comporta como o método clone() padrão do Object do Java).

Porém, a cópia de Foo será destruída ao final do método doSomething, pois a variável local criada no parâmetro será destruída, fazendo com que o código delete obj seja executado. Ao ser executado, isso irá destruir o objeto apontado por obj. Assim, fora do método, a variável foo agora está apontando para um objeto que não existe mais, fazendo o método getName() crashear.

Há três formas de resolver esse problema:
1. Desabilitar o construtor de cópia e o operador de =. Para fazer isso, basta declara-los sem implementação na sessão private da sua classe - isso gerará um erro de compilação caso você tente fazer uma cópia acidental. Na verdade, é uma boa prática fazer isso sempre, só removendo do private se você efetivamente for se preocupar com cópia na classe.
2. Implementar o construtor de cópia e o operador de = para que façam a cópia do que é apontado;
3. Usar um smart pointer, como o std::shared_pointer, caso você queira compartilhar, ou um std:unique_ptr, caso não queira.

Eu elaborei uma aula sobre boas práticas de C++, que você pode baixar aqui:
https://www.dropbox.com/s/8z0y58q2jqvgums/Boas%20Pr%C3%A1ticas%20de%20Programa%C3%A7%C3%A3o.pdf
Explica algumas pegadinhas como essa. Essa é uma das aulas que ministro na pós de jogos. Mas os slides não são lá tão úteis sem eu explica-los, hehehe.

A aula é baseada nos livros Effective C++ e More Effective C++, do Scott Meyers. Leitura obrigatória depois que você vencer a sintaxe básica.

construtores sao definidos no .h ou no .cpp?

Você faz a declaração no .h e a definição no .cpp.

variaveis estaticas sao inicizalidas no .h ou no .cpp?

Novamente, declara-se no .h e define-se no .cpp.

No .h você só colocará código se:
- A função for inline;
- Você usar templates.

Lembre-se também que código escrito no .h é público, pois não é compilado.

faeldix

bkna vinny… meio do ano teremos um evento aqui na universidade e como tenho boa relação com a coordenação do curso, poderíamos ver a possibilidade de lhe trazer, seria uma honra.

Ainda por cima me ajudaria pois estou criando um jogo para Android que inclusive estarei apresentando no Computer on The Beach em floripa esse ano.

ViniGodoy

Só para saber, quando você fala em “aqui” está falando de onde?

faeldix

Universidade Ceuma
São Luis - MA

ViniGodoy

Fica só a 2600km daqui. :roll:

faeldix

Sim sim, isso ta longe de ser um problema, a nao ser que vc nao possa viajar nos ou ter medo de aviao.

ViniGodoy

Sem problemas com avião.

E
ViniGodoy:
Basicamente a mesma coisa, mas não vejo muito por que ser desse jeito: Vai ficar assim:
//Teste.h
class Teste {
private:
	Teste t;

	Teste(){}
...

void Teste::imprime(){
	cout &lt;&lt; &quot;Olá&quot; &lt;&lt; endl;
}
Náo é à toa que este post foi editado 5 vezes - em C++ certas coisas que se fazem em Java tem de ser escritas de forma sutilmente diferente. Por exemplo,
class Teste {
private:
    Teste t;
...
};
que superficialmente é equivalente ao seguinte código Java:
class Teste {
    private Teste t = new Teste();
...
};
Este código em C só vai compilar se sizeof(Teste) == 0. O que ocorre se sizeof(T) for diferente de 0? Por exemplo,
class Teste {
private: Teste t;
public: int i;
};

Teste x;
Qual será o valor de sizeof (x)? O código C ++ que é equivalente ao código Java que postei aqui é:
class Teste {
private:
    Teste *t;
...
    ...
};
ViniGodoy

Faltou escrever o modificador static lá. Ou deixar com ponteiros, como você fez.

faeldix

ja tentei conseguir entender qual o problema.. mas realmente ta complicado..
to brincando um pouquinho com ponteiros e fiz o seguinte teste:

header

class Pointer {
	int *valor;
public:
	Pointer();
	~Pointer();
	Pointer(const Pointer&);

	void setValor(int);
	int getValor() const;
};

cpp

#include <iostream>
#include "teste.h"

using namespace std;

Pointer::Pointer(){
	valor = new int(0);
}

Pointer::~Pointer(){
	cout << "Destruindo o objeto de valor: " << endl;
}

Pointer::Pointer(const Pointer& p){
	valor = p.valor;
}

void Pointer::setValor(int value){
	valor = &value;
}

int Pointer::getValor() const {
	return *valor;
}

e main

#include <iostream>
#include "teste.h"

using namespace std;

int main(){

	Pointer p;
	p.setValor(13);

	Pointer p2(p);
	cout << p2.getValor() << endl;

	p.setValor(12);
	
	cout << p2.getValor() << endl;

	return 0;	
}

saida:

rafael@rafael-note:~/Área de Trabalho/C/C++$ ./a.out -[telefone removido] 12 Destruindo o objeto de valor: Destruindo o objeto de valor:

de onde saiu esse: -[telefone removido]

visto que da segunda vez que utilizo o metodo set funciona????
estranho :shock: :shock:

E

Não não não não não faça isto aqui.

void Pointer::setValor(int value){  
    valor = &value;  
}

O que é que você fez?
Você pegou um endereço de uma variável no stack (não no heap) e o atribuiu dentro de uma variável de instância de um seu objeto.
Como você deve saber, parâmetros e variáveis locais residem ou no stack, ou então em registradores da CPU, dependendo do grau de otimização do seu compilador.
Você poderia até passar esse endereço para um outro método dentro desse método que você escreveu, porque o tempo de vida é dentro da execução desse método.
Mas desse jeito que você fez, o programa saiu do método setValor e ficou com um endereço que aponta para uma área que você não tem mais posse.

Nunca faça isso. Nunca nunca nunca. Você vai tomar na cabeça até entender o que estou falando.

faeldix

entanglement:
Não não não não não faça isto aqui.

void Pointer::setValor(int value){  
    valor = &value;  
}

O que é que você fez?
Você pegou um endereço de uma variável no stack (não no heap) e o atribuiu dentro de uma variável de instância de um seu objeto.
Como você deve saber, parâmetros e variáveis locais residem ou no stack, ou então em registradores da CPU, dependendo do grau de otimização do seu compilador.
Você poderia até passar esse endereço para um outro método dentro desse método que você escreveu, porque o tempo de vida é dentro da execução desse método.
Mas desse jeito que você fez, o programa saiu do método setValor e ficou com um endereço que aponta para uma área que você não tem mais posse.

Nunca faça isso. Nunca nunca nunca. Você vai tomar na cabeça até entender o que estou falando.

eu vinha qui justamente postar isso… mas nao to entendendo… pq funcionou com o valor 12 oO’
visto que a variavel morre no final…

E

Bem vindo a Hogwarts - mais conhecido como “mundo da programação em C++”.

De fato, você pode ter esse tipo de comportamento - algo que aparentemente está certo, mas que na verdade está acessando memória indevidamente.

Como eu disse, é o tipo de coisa que é fácil de fazer mas que é muito difícil de corrigir. Muitas vezes você precisa ter o conhecimento da Hermione e a sorte do Harry Potter para descobrir certos problemas - é claro que existem várias ferramentas que permitem descobrir esses problemas, mas na prática nem sempre você tem o tempo ou o dinheiro para poder rodar as ferramentas e descobrir os problemas.

faeldix

olha isso:

Pointer p;
	p.setValor(13);
	
	Pointer p2(p);
	cout << p2.getValor() << endl;

	p.setValor(12);
	cout << p2.getValor() << endl;
	cout << p2.getValor() << endl;
	cout << p2.getValor() << endl;

resultado:

rafael@rafael-note:~/Área de Trabalho/C/C++$ ./a.out 
Criando um objeto
-[telefone removido]
12
134514112
134514112

é como se a variavel ainda estivesse no stack.. o metodo de impressão foi mais rapido.. (ou estou viajando?)

ViniGodoy

Cuidado… seu construtor de cópia está copiando o ponteiro, não o valor do ponteiro.

O certo seria:

Pointer::Pointer(const Pointer& p){ valor = new int(); *valor = *(p.valor); }

O setValor também está errado. Você quer alterar o valor que está no local onde o ponteiro aponta, para uma cópia do valor que veio por parâmetro:

void Pointer::setValor(int value){ *valor = value; }

Por fim, falou dar delete valor no seu destrutor.

E

A variável ainda está no stack, mas pode ser que alguma outra atividade acabe “estragando” esse valor que está no stack (e é por isso que você tem esses resultados bizarros).

Experimente recompilar seu código com outro compilador, e veja que os resultados serão diferentes.

E

Construtores de cópia podem copiar ponteiros se eles mantiverem uma contagem de referências, o que está fora do escopo para você que está ainda aprendendo a dar tiros no pé com C++ :slight_smile:
Normalmente você deve fazer uma cópia da coisa apontada, como indicou o Vinicius Godoy.

Considero o uso “cru” de ponteiros como o “goto” e as threads do Java - se você não souber o que está fazendo, pode fazer um monte de besteiras (como aquele policial que matou o colega dele que estava em uma motocicleta e fingiu que era um bandido).

Se souber, ainda vai fazer besteiras de vez em quando :slight_smile:

É o “zen” da programação - você aprende a usar ponteiros, apenas para poder saber que não deve usá-los sem proteção adequada. É para dar um monte de tiros no pé para aprender mesmo :slight_smile:

faeldix

na verdade… eu fiz desse jeito de proposito mesmo… so esqueci que a variavel passada por parametro iria morrer ao fim do metodo…
mas a ideia mesmo era modificar uma variavel de um objeto através de outro…

mas pessoal… aprecio muito a ajuda de vcs…
obrigado a todos :smiley:

ViniGodoy

Eu sempre digo que um bom programador C++ sabe usar ponteiros. E um programador C++ excelente sabe não usa-los.

Criado 6 de janeiro de 2013
Ultima resposta 21 de jan. de 2013
Respostas 36
Participantes 4