Marcelo Cohen, FACIN - PUCRS
Agrupam-se as funções/classes relacionadas em módulos. Para cada módulo são utilizados dois arquivos:
Para usar uma função/classe em outro módulo, basta então incluir o arquivo .h correspondente
Para exemplificar, criaremos uma biblioteca simples de funções matemáticas. Primeiro, escrevemos os protótipos das funções no arquivo bibfunc.h:
float fatorial(int v);
float somatorio(int v);
...
Porém, se por engano esse arquivo for incluído mais de uma vez, haverá uma duplicidade de todas as definições. Como isso pode acontecer facilmente, acrescentamos 3 linhas no arquivo .h:
#ifndef BIBFUNC_H
#define BIBFUNC_H
float fatorial(int v);
float somatorio(int v);
...
#endif
A seguir, escrevemos o arquivo bibfunc.cpp, que contém a implementação das funções definidas anteriormente.
#include "bibfunc.h"
float fatorial(int v) {
int res = 1;
for(int i=1; i<=v; i++)
res = res * i;
return res;
}
...
Finalmente, o programa principal deve ser implementado em um arquivo separado, como por exemplo main.cpp:
#include <iostream>
#include "bibfunc.h"
using namespace std;
int main()
{
cout << "Digite um valor: ";
int v;
cin >> v;
cout << "Fatorial: " << fatorial(v);
cout << "Somatório: " << somatorio(v);
...
}
Para compilar o programa completo, é preciso indicar todos os fontes na linha de comando:
Porém, essa estratégia tem algumas implicacões:
Para um programa tão pequeno, isso não faz tanta diferença...
Mas se o programa for composto por muitos módulos, compilá-los separadamente pode ter um custo considerável, além de não ser muito prático.
Um Makefile é um roteiro de compilação:
O nome do arquivo deve ser preferencialmente Makefile.
Para utilizá-lo, basta digitar o comando make no terminal.
all: main
main: main.o bibfunc.o
g++ -o main main.o bibfunc.o
main.o: main.cpp
g++ -c main.cpp
bibfunc.o: bibfunc.cpp
g++ -c bibfunc.cpp
Os comandos de compilação não parecem meio repetitivos ? Podemos melhorar o Makefile, criando regras genéricas:
CXXFLAGS = -Wall -g # Opções do compilador: todos warnings e debug info
PROG = main
FONTES = main.cpp bibfunc.cpp
OBJETOS = $(FONTES:.cpp=.o)
$(PROG): $(OBJETOS)
g++ $(CXXFLAGS) $(OBJETOS) -o $@
clean:
-@ rm -f $(OBJETOS) $(PROG)
Mas e como especificar dependências automáticas para os .h ?
Uma forma é incluir uma regra que use o comando makedepend:
...
clean:
-@ rm -f $(OBJETOS)
depend:
makedepend -- ${CFLAGS} -- ${FONTES}
Ao digitarmos make depend, o comando makedepend será ativado, e verificará as dependências de cada módulo, incluindo estas no final do próprio Makefile:
...
depend:
makedepend -- ${CFLAGS} -- ${FONTES}
# DO NOT DELETE
main.o: bibfunc.h
bibfunc.o: bibfunc.h
Com classes, o princípio é o mesmo: agrupam-se as classes relacionadas em módulos e para cada módulo são novamente utilizados dois arquivos:
Para usar uma classe em outro módulo, basta então incluir o arquivo .h correspondente
Para exemplificar, utilizaremos uma classe Carro, que permite controlar o abastecimento e movimentação de um veículo. Primeiro, escrevemos a declaração da classe Carro no arquivo Carro.h:
#ifndef CARRO_H
#define CARRO_H
class Carro {
public:
Carro();
bool abastecer(float litros);
bool mover(float km);
float getTanque();
float getDistancia();
private:
float tanque;
float distancia;
};
#endif
A seguir, escrevemos o arquivo Carro.cpp, que contém a implementação dos métodos da classe Carro.
#include "Carro.h"
Carro::Carro() {
tanque = 0;
distancia = 0;
}
bool Carro::abastecer(float litros) {
if(tanque + litros <= 50)
tanque += litros;
...
}
...
Finalmente, o programa principal deve ser implementado em um arquivo separado, como por exemplo main.cpp:
#include <iostream>
#include "Carro.h"
using namespace std;
int main()
{
Carro carro1, carro2;
carro1.abastecer(20);
carro2.abastecer(30);
bool moveu1 = carro1.mover(200);
if(moveu1==false)
cout << "Carro 1: Não há combustível suficiente!" << endl;
...
}
Já existem alguns sistemas alternativos ao make: