Você está na página 1de 38

Física Computacional

Introdução ao ambiente linux, C++ e à resolução


de problemas físicos

Fernando Barão

12 de Março 2022
Conteúdo

O ambiente de programação Linux/macOS . . . . . . . . . . . . . . . . . . . . . . . . . 5


Introdução ao ambiente de programação Linux . . . . . . . . . . . . . . . . . . . 5
Compilação de C++ em Linux/macOS . . . . . . . . . . . . . . . . . . . . . . . . . 7
Compilação de C++ e utilização da biblioteca ROOT . . . . . . . . . . . . . . . . . 10
Programação em C++: exemplos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
A utilização de endereços de variáveis (pointers) em C++ . . . . . . . . . . . . . . 12
Passagem de argumentos de funções por cópia, referência e endereço: função factorial 13
Leitura de um ficheiro de dados em C++ . . . . . . . . . . . . . . . . . . . . . . . . 15
Estruturas em C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Programação em C++: biblioteca STL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Funções lambda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Criação do vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Obter o tamanho do vector . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
Aceder ao conteúdo do vector . . . . . . . . . . . . . . . . . . . . . . . . . 23
Adicionar elementos a um vector . . . . . . . . . . . . . . . . . . . . . . . 23
Remover elementos do vector . . . . . . . . . . . . . . . . . . . . . . . . . 24
Variar o tamanho do vector . . . . . . . . . . . . . . . . . . . . . . . . . . 24
Criação de matrizes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
Números complexos em C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Criação de um mapa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Aceder aos elementos do maps e ciclo . . . . . . . . . . . . . . . . . . . . . 28
Funções várias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Utilização de algoritmos em vector . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Imprimir um vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Copiar um vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Ordenar um vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Soma dos elementos de um vector . . . . . . . . . . . . . . . . . . . . . . 32
Transformar um vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
Programação em C++: classes e objectos . . . . . . . . . . . . . . . . . . . . . . . . . . 33
Estruturas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

3
12 de Março 2022 Fis Computacional/IST (2021-22, P3)

ROOT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Documentação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Gráficos: representação de pontos . . . . . . . . . . . . . . . . . . . . . . . . . . 36

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

O ambiente de programação Linux/macOS

Introdução ao ambiente de programação Linux

Para que serve o sistema operativo do computador


Um computador consiste num conjunto de componentes electrónicos ( hardware ) que permitem
o desempenho de várias funções. A execução das operações no computador são definidas por
humanos, possuindo o computador um teclado e um monitor que tornam fácil a sua programação:
A título de curiosidade, na primeira metade da década de 1980 ainda se usavam cartões perfurados
no IST para programar computadores (que nessa altura não eram pessoais, mas dispositivos algo
volumosos!)
O sistema operativo permite comunicar com o computador. A forma mais básica é através da
interface de comandos (criação de uma janela terminal ) podendo do entanto ser utilizada a
interface gráfica.
Linux/macOS
O sistema operativo UNIX foi criado em 1969 por Ken Thomsen e Dennis Ritchie (laboratórios
Bell, USA), usando a linguagem de programação C. A popularização deste sistema operativo e a
emergência do computador pessoal aliado ao facto de ser necessário uma licença paga para o uso
do UNIX, fez emergir soluções alternativas de sistemas abertos (gratuitos) UNIX, o mais popular dos
quais foi o sistema Linux criado por Linus Torvalds em 1991. Nos dias que correm existem versões
Linux gratuitas (Ubuntu, centOS, ...) e não gratuitas.
As razões do sucesso do sistema Linux resumem-se no facto de permitir múltiplos utlizadores (e ser
portanto multi-tarefa) e de ser fácil de utilização (acreditem!).
O sistema operativo dos computadores Apple ( macOS ) é na sua essência um sistema Unix,
podendo haver no entanto particularidades.
Comandos básicos Linux
No sistema Linux a janela terminal é a aplicação (um programa de facto codificado em C/C++)
que permite ao utilizador interagir com o computador.
Como obter o terminal nos diferentes sistemas:

windows: deve abrir a aplicação Ubuntu (após a sua instalação de acordo com o descrito na
página da disciplina).

macOS e Linux: abrir a aplicação terminal

Uma vez aberto o terminal, a pasta onde se encontra deve ser a pasta de entrada do utlizador.
Admitamos nos exemplos de comandos que se seguem que o nome do utilizador é silva .

• saber o caminho completo da pasta onde se encontra

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

pwd

• criar na pasta onde está o sistema de pastas de Física Computacional

mkdir main src lib bin

• listar o conteúdo da pasta onde se encontra

ls -l

• listar o conteúdo da pasta incluindo os ficheiros invisíveis (no linux estes possuem um nome
que se inicia com . )

ls -la

• ir para a pasta main

cd main

• ir para a pasta de entrada do utilizador

cd

cd ~silva

• remover o ficheiro a.C que se encontra na pasta main/

rm a.C

• copiar o ficheiro a.C para b.C

cp a.C b.C

• remover a pasta teste com todo o seu conteúdo (certifique-se que é mesmo isto que
pretende!)

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

rm -fr teste

No caso de estar a trabalhar no Ubuntu instalado no windows , os seguintes comandos


aplicam-se adicionalmente aos descritos anteriormente:

• listar as pastas do Windows a partir do Ubuntu

ls -l /mnt/c

Notas finais:

• é conveniente que os nomes das pastas ou ficheiros criadas em Linux não contenham
espaços, ou caracteres acentuados.

Compilação de C++ em Linux/macOS

Comecemos por criar um pequeno programa em C++. Para tal teremos que definir que quadro de
programação iremos usar. Há duas hipóteses possíveis:

• utlizar um Integrated Development Environment (IDE) onde será possível reali-


zar o código C++, compilar e fazer mesmo debugging (despiste de erros). Eis alguns exemplos
de IDE’s:

– vscode : visual studio está disponível para vários sistemas


– Xcode : somente disponível em macOS link
– Code::Blocks : disponível para Linux,macOS e Windows link
– Eclipse : disponível para Linux,macOS e Windows link

• utilizar um editor de texto para programar e a linha de comandos para compilar

Que editor escolher?

A resposta a esta questão não será nunca única porque será sempre uma escolha muito pessoal.
Listam-se de seguida alguns editores:

• vim : editor clássico de texto em Unix e muito versátil, disponívels em todos os sistemas link
• emacs : editor muito versátil, disponível em todos os sistemas link
• xemacs : editor emacs com interface gráfica link
• sublime : disponível para todos os sistemas link
• atom : disponível para todos os sistemas link
• nano : editor simples

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

O primeiro programa C++

Um programa ou função escrita em C++ é um conjunto de instruções codificadas em linguagem


simbólica C++, que podem recorrer a classes ou funções exteriores à linguagem. Por exemplo no
programa abaixo iremos imprimir uma frase no ecran usando cout e o fim de linha endl .
Estas funções/classes exteriores à linguagem necessitam sempre de ser declaradas antes de serem
usadas.

Usando o editor que achar mais conveniente ou IDE, realizemos o nosso primeiro programa de C++
que será implementada no ficheiro teste.C , existente na pasta main/ .

1 #include <iostream>
2 using namespace std;
3 int main() {
4 cout << "o meu primeiro programa" << endl;
5 }

A linha 1 contém a instrução #include <iostream> . Esta instrução permite incluir no có-
digo as declarações existentes no ficheiro iostream que o compilador sabe onde encontrar no
computador (por isso usámos <> ).

A linha 2 contém a instrução using namespace std; . Esta instrução permite selecionar
por defeito o domínio ( namespace ) std onde se encontram definidas as classes da bi-
blioteca STL inerente ao C++. Senão existisse esta instrução teríamos que colocar no código,
std::cout << ... << std::endl .

A linha 3 contém a instrução int main() { : um programa C++ necessita sempre de definir a
função main() , de outra forma não funcionará. Note também a existência da chaveta { que
assinala o início do bloco de código da função.

A linha 4 imprime no ecran a frase definida.

A linha 5 encerra o bloco da função.

Como compilar o programa?

Verifique primeiramente que possui o compilador g++ correndo o seguinte comando que lhe
dará a versão do compilador que possui instalada:

g++ -v

Se instalou o Ubuntu no Windows e não possui o compilador, corra os seguintes comandos:

sudo apt update && sudo apt upgrade


sudo apt install build-essential

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

A produção do executável do programa ( .exe ) comporta sempre a realização de duas fases:

• compilação (incluindo pré-compilação)


• linkagem

Para compilarmos o programa, usando a revisão da linguagem de 2011, fazemos:

g++ -c -std=c++11 main/teste.C

O uso da opção -c realiza somente a compilação do código. Liste os ficheiros existentes na pasta
em que realizou o comando. Caso a compilação tenha ocorrido sem erros, verificará que existe
um ficheiro teste.o . Gostaríamos no entanto que este ficheiro binário (não legível por nós)
fosse criado na pasta bin/ . Para isso alteramos o comando anterior de forma a conter o nome
do output :

g++ -c -std=c++11 main/teste.C -o bin/teste.o

Se pretender ver o aspecto do ficheiro que é criado contendo as declarações ìostream , faça:

g++ -E main/teste.C

Para obter o programa executável ( teste.exe ) que permitirá realizar a tarefa definida no
programa, retira-se a opção -c :

g++ -std=c++11 main/teste.C -o bin/teste.exe

No caso do programa usar funções exteriores ( func1.C , func2.C ) definidas e existentes na


pasta src/ , a compilação do programa seria feita da seguinte forma:

g++ -std=c++11 main/teste.C src/fun1.C src/func2.C -o bin/teste.exe

No caso de termos usado ficheiros header ( func1.h , func2.h ) contendo as declarações


das funções e existentes na pasta src/ , teríamos que fornecer na instrução de compilação a
localização dos ficheiros header . Isso seria feito com o auxílio da opção de compilação -I :

g++ -std=c++11 main/teste.C src/fun1.C src/func2.C -I src \


-o bin/teste.exe

Nota: no comando anterior usámos \ para indicar a continuação do comando na linha seguinte.
Isso não será necessário, se inserir o comando numa só linha no computador.

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

Compilação de C++ e utilização da biblioteca ROOT

Como utilizar ROOT na compilação de programas?


Após a instalação de ROOT, este pode ser utilizado directamente escrevendo na linha de comandos
do terminal,

root

devendo aparecer a informação seguinte no ecran (indicando neste caso que a versão 6.26 se
encontra instalada):

Figura 0.1: ROOT prompt 6.26

O ROOT possui um interpretador de C++ podendo por isso introduzir código C++ directamente em
ROOT.
Mas a utilização de ROOT sugerida no âmbito do curso de Física Computacional, é como biblio-
teca, podendo os programas que desenvolvamos utilizar os inúmeros objectos que se encontram
definidos na biblioteca.
ROOT possui um programa utilitário que permite ligar facilmente o programa com ROOT. Trata-se
do comando root-config .
Escrevendo na linha de comandos do terminal,

root-config --cflags

obtemos a informação de quais as opções do compilador g++ usadas na compilação de ROOT


e ainda a localização dos seus ficheiros header. Podem verificar que o código ROOT terá sido
compilado com a opção c++14 ou mesmo c++17.
Por outro lado, escrevendo na linha de comandos do terminal,

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

root-config --libs

temos acesso à biblioteca de ROOT (que não é mais que o código compilado .o de ROOT reunido
em vários ficheiros cuja extensão é .so).
Admitamos que desenvolvemos o programa main/tvector.C onde se encontram chamadas a
funções/classes de ROOT. Para compilar e linkar o programa com a biblioteca de ROOT, faríamos:

g++ `root-config --cflags` main/tvector.C `root-config --libs` -o


,→ bin/tvector.exe

O uso das pelicas no comando root-config ... permite que o resultado do comando apa-
reça imediatamente na linha de comandos.

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

Programação em C++: exemplos

A utilização de endereços de variáveis (pointers) em C++

As variáveis declaradas num programa são armazenadas na memória do computador durante a sua
execução. A linguagem C++ permite aceder ao endereço de memória onde a variável se encontra.
Veremos de seguida um exemplo comentado de um programa onde se se utiliza o endereço de
variáveis.

1 #include <iostream>
2 using namespace std;
3 #include <cstdio> // printf
4

5 int main() {
6

7 // define array of 10 elements


8

9 double a[] {0,1,2,3,4,5,6,7,8,9};


10 for (int i=0; i<10; ++i) {
11 cout << "i=" << i << " | " << a[i] << " | ";
12 a[i] += 10;
13 cout << a[i] << endl;
14 }
15

16 // access array elements using address of first array element


17

18 double* p = &a[0];
19 for (int i=0; i<10; i++, p++) {
20 printf("a[%d]=%g | %p | %g \n", i, a[i], p, *p );
21 }
22

23 // example of auto declaration


24

25 auto x=1; auto y=1.;


26 cout << typeid(x).name() << endl;
27 cout << typeid(y).name() << endl;
28 }

Comentários:

• linhas 9-14:
Define-se um array a do tipo double com 10 elementos, onde se colocam os números
0, 1, 2, · · · , 9

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

• linhas 18-21:
Realiza-se de seguida um ciclo sobre todos os valores do array , imprimindo o seu
índice e o seu valor e ainda somando a cada um dos valores do array o valor 10. Note o
incremento do endereço realizado no ciclo e ainda o acesso ao valor da variável através da
desreferenciação do seu endereço *p .

• linhas 25-27:
Mostra-se a utilização da declaração auto que permite definir variáveis em algumas situa-
ções.

Passagem de argumentos de funções por cópia, referência e endereço: função


factorial

O exemplo de programa que se segue mostra como chamar uma função que calcula o factorial de
um número. Neste exemplo aproveitamos para estruturar o código C++ em três ficheiros:

• programa principal: main/rfactorial.C


• implementação da função factorial: src/factorial.C
• declaração da função factorial: src/factorial.h

programa principal: rfactorial.C

1 #include <iostream>
2 using namespace std;
3

4 #include "factorial.h"
5

6 int main() {
7

8 // defining array of integers and printing their values


9

10 int a[] {4, 6, 7};


11

12 // calculating factorial returning result by copy


13

14 for (int i=0; i<3; i++) {


15 cout << a[i] << " | " << factorial(a[i]) << endl;
16 }
17 cout << endl;
18

19 // calculating factorial returning void and getting result by reference


20

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

21 for (int i=0; i<3; i++) {


22 long int result=1;
23 factorial(a[i], result);
24 cout << a[i] << " | " << result << endl;
25 }
26 cout << endl;
27

28 // calculating factorial returning void and getting result by address


,→ (pointer)
29

30 for (int i=0; i<3; i++) {


31 long int result=1;
32 factorial(a[i], &result);
33 cout << a[i] << " | " << result << endl;
34 }
35 cout << endl;
36 }

função factorial: factorial.C

1 include <iostream>
2 using namespace std;
3

4 long int factorial(int n) {


5 cout << __PRETTY_FUNCTION__ << endl;
6 long int a = 1;
7 for (int i=n; i>0; i--) {
8 a *= i;
9 }
10 return a;
11 }
12

13 void factorial(int n, long int& result) {


14 cout << __PRETTY_FUNCTION__ << endl;
15 for (int i=n; i>0; i--) {
16 result *= i;
17 }
18 }
19

20 void factorial(int n, long int* result) { // address


21 cout << __PRETTY_FUNCTION__ << endl;
22 for (int i=n; i>0; i--) {

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

23 (*result) *= i;
24 }
25 }

declarações da função factorial: factorial.h

1 long int factorial(int);


2

3 void factorial(int, long int&);


4

5 void factorial(int, long int*);

Leitura de um ficheiro de dados em C++

O exemplo de programa que se segue mostra como lêr um ficheiro de uma série temporal de dados
tempo e amplitude. As operações que iremos realizar são as seguintes:

• abrir o ficheiro usando a classe ifstream


• lêr as linhas do ficheiro que contêm comentários (duas linhas)
• lêr os dados do ficheiro para dois arrays de dimensão 2000

programa principal: fourier.C

1 #include <fstream>
2 #include <iostream>
3 #include <string>
4 using namespace std;
5

6 int main() {
7

8 // open data file


9

10 string d("/mnt/c/Users/silva/DATA/");
11 ifstream F(d+"tDFT.dat");
12 cout << "file: " << d+"tDFT.dat" << endl;
13

14 /*
15 data file structure:
16 - 2 first lines with comments
17 - data after: time, amplitude
18 */

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

19

20 // read first 2 lines to place reading pointer at file 3rd line


21

22 string s;
23 for (int i=0; i<2; i++) {
24 getline(F, s);
25 cout << s << endl;
26 }
27

28 // read data to 2 arrays


29

30 // ... declare arrays


31 const int n = 2000;
32 double t[n], A[n];
33

34 int i=0; // counter


35 while (F >> t[i] >> A[i]) {
36 cout << t[i] << " | " << A[i] << endl;
37 i++;
38 }
39 cout << "counter:" << i << endl;
40 }

Comentários:

• linhas 34-39:
a leitura dos dados faz-se campo a campo do ficheiro (os números no ficheiro estão separados
por um espaço e por isso são distinguíveis) para elementos do array do tipo double

Estruturas em C++

A declaração struct em C++ é uma maneira cómoda de organizar a informação em variáveis


com tipos misturados. Por exemplo pensemos no objecto planet do sistema solar. Para o
caracterizarmos necessitamos guardar várias características, como por exemplo:

• nome: tipo de variável string


• massa: tipo de variável double
• raio: tipo de variável double
• etc.

Podemos ainda incluir funções no interior da estrutura e que poderão ser usadas pelos objectos
planet .

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

A declaração da estrutura far-se-á num ficheiro .h existente na pasta src/ .


Veremos mais adiante no curso que a noção de classe permitirá estender a estrutura, enquanto
objecto de programação em C++.
declaração do obecto planeta

1 #include <iostream>
2 using namespace std;
3

4 struct planet {
5 string nome; // planet name
6 double mass; // planet mass (kg)
7 double radius; // planet mean radius (m)
8 double distance; // planet distance to sun (m)
9 void print(){
10 cout << "nome=" << nome << " | mass=" << mass << endl;
11 };
12 };

No programa principal que se segue faremos uso do objecto planeta definido através da estrutura.
Este programa encontra-se localizado na pasta main/ . Não esquecer de incluir antes do porgrama
principal a declaração da estrutura. Fá-lo-emos através da inclusão do ficheiro planet.h que
contém a declaracção.
A inicialização da estrutura pode ser feita de diferentes formas como se mostra de seguida.
programa principal: rplanet.C

1 #include <iostream>
2 using namespace std;
3

4 #include "planet.h"
5

6 int main() {
7

8 // define mars planet before c++20


9

10 planet mars;
11 mars.nome = "mars";
12 mars.mass = 6.4171E23;
13 mars.radius = 3389.5E3;
14

15 mars.print();

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

16

17 // define mars planet after c++20


18

19 planet mars2 = {"mars", 6.4171E23, 3389.5E3};


20 mars2.print();
21

22 }

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

Programação em C++: biblioteca STL

Funções lambda

As funções lambda na linguagem C++ foram introduzidas na revisão de 2011. E estão amplamente
ligadas à biblioteca STL (standard template library), porque podem ser usadas de forma fácil em
algoritmos envolvendo containers do STL.

A função lambda tem na sua definição possibilidade de capturar variáveis definidas fora dela,
fazer passar argumentos e definir o retorno da função. Regra geral não temos que definir o retorno,
porque o compilador da linguagem será capaz de inferir o tipo de objecto que está a ser retornado.

Comecemos por um exemplo muito simples que consiste em produzir uma função que imprima
uma frase.

1 #include <string>
2 #include <iostream>
3 using namespace std;
4

5 int main() {
6

7 // [] capture specification: tells the compiler we are creating a


,→ lambda function
8 // () argument list
9 // Note: lambda function requires explicit type on arguments, auto
,→ cannot be used
10

11 // define lambda function f1: no parameters, no return


12 auto f1 = [](string s) {
13 cout << s << endl;
14 };
15

16 // call function
17 f1();
18 }

No próximo exemplo, definimos um texto e pediremos à função que encontre um determinado


padrão no texto. Passaremos assim por captura o texto (por cópia) e o padrão de texto a procurar,
por argumento (por cópia também). Retornaremos um bool true caso encontremos o padrão
ou false , caso contrário.

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

1 #include <string>
2 #include <iostream>
3 using namespace std;
4

5 int main() {
6

7 /*
8 - define lambda function that searchs for a pattern in variable name
9 that is defined outside function
10 - we need to capture variables outside function (by copy)
11 */
12 std::string name="teste name então o que fazes por aqui";
13 auto f2 = [name](std::string patt) {
14 return name.find( patt ) != std::string::npos; // pos is a static
,→ var
15 };
16

17 cout << "teste found? : " << f2("teste") << endl;


18 cout << "xx found ? :" << f2("xx") << endl;
19 }

Na próxima função lambda iremos calcular o comprimento de onda dada a frequência. Procedemos
à captura da velocidade da luz e passamos a frequência como argumento da função.

1 #include <string>
2 #include <iostream>
3 using namespace std;
4

5 int main() {
6

7 // physical constants
8 const double h = 6.62607015E-34; // Planck constant kg.m2.s-1
9 const double c = 299792458.; // light speed m/s
10 const double eV2J = 1.6022E-19; // 1eV in Joules
11

12 // compute wavelength (nm) given a freq (Hz)


13 auto fwl = [c](double freq){
14 return c/freq*1E9; // nm
15 };
16

17 cout << "fwl(3.E11)=" << fwl(3.E11) << " nm" << endl;
18

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

19 }

Por último, veremos como poderíamos calcular o o comprimento de onda dada a frequência,
mas em lugar do retorno da função, capturamos a variável comprimento de onda por referência,
podendo portanto modificá-la.

1 #include <string>
2 #include <iostream>
3 using namespace std;
4

5 int main() {
6

7 // physical constants
8 const double h = 6.62607015E-34; // Planck constant kg.m2.s-1
9 const double c = 299792458.; // light speed m/s
10 const double eV2J = 1.6022E-19; // 1eV in Joules
11

12 // compute wavelength (nm) given a freq (Hz)


13 double wl;
14 auto fwl = [c, &wl](double freq){
15 wl = c/freq*1E9; // nm
16 };
17 fwl(3.E11);
18 cout << "wl=" << wl << " nm" << endl;
19 }

vector

O vector é uma estrutura dinâmica cujo número de elementos pode variar ao longo do programa. O
tipo de elementos do vector necessita de ser definido aquando da sua criação. De seguida veremos
como:

Criação do vector

• criar um vector de int vazio


• criar um vector de double e inicializado com elementos

1 // create vector with 5 elements = 10


2 vector<int> vi(5,10);

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

4 // create vector of double with 5 elements


5 vector<double> vd {1,2,3,4,5};

Iremos de seguida criar um vector vazio e adicionar elementos através de um ciclo. De notar que
para podermos alterar os elementos do vector teremos que aceder a estes através do mecanismo
de referência:

1 // create vector with 10 positions


2 vector<int> vi(10);
3 int j=0;
4 for ( auto& e: vi) {
5 e = j;
6 j++;
7 }

Criar um vector a partir de um array ,

1 int myInt[] {1,2,3,4,5};


2 vector<int> v(5);
3 copy(myInt, myInt+5, v.begin());

Criar um vector a partir de um outro vector ,

1 vector<int> vi {1,2,3,4,5};
2 vector<int> v2(vi);

Por último vejamos como criar um vector de objectos. Na secção anterior abordámos as estruturas
que são meios fáceis de criar objectos. De seguida criaremos um vector de objectos planet
definidos anteriormente e inicializados tal como descrito anteriormente,

1 // create vector of planets


2 vector<planet> P { {"mars", 6.4171E23, 3389.5E3}, {"earth", 5.97237E24,
,→ 6356.752E3} };
3

4 // loop on vector and print planets


5 for (auto e: P) {
6 e.print();
7 }

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

Obter o tamanho do vector

1 vector<double> vi {1,2,3,4,5};
2 cout << vi.size() << endl;

Aceder ao conteúdo do vector

Os elementos do vector podem ser acedidos directamente através do operador [] ,

1 vector<double> vi {1,2,3,4,5};
2 for (int i=0; i<int(vi.size()); i++) {
3 v[i] *= 10;
4 cout << vi[i] << endl;
5 }

ou usando o seu iterador,

1 for (std::vector<int>::iterator it = vi.begin(); it != vi.end(); ++it) {


2 *it *= 10;
3 cout << *it << endl;
4 }

Por exemplo, para se obter o iterador para o primeiro eçemento do vector e utilizando a
declaração auto ,

1 auto it = vi.begin();

Adicionar elementos a um vector

Adicionemos dois novos elementos ao vector: 12 e 22.

1 vi.push_back(22);
2 vi.push_back(12);

Vamos adicionar os elementos do array b ao final do array a :

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

1 vector<int> a {1,2,3,4,5,6,7,8,9};
2 vector<int> b {12,13,14,15};
3 a.insert(a.end(), b.begin(), b.end());

Remover elementos do vector

Se pretendermos remover todos os elementos do vector ,

1 vector<int> a {1,2,3,4,5,6,7,8,9};
2 a.clear();

Para remover elementos do vector , podemos usar o método erase() . Após esta operação,
o vector reduz o seu tamanho.
Por exemplo, se pretendermos remover o 4º elemento do vector ,

1 vector<int> a {1,2,3,4,5,6,7,8,9};
2 a.erase(a.begin()+3);

Se pretendermos remover o três primeiros elementos do vector ,

1 vector<int> a {1,2,3,4,5,6,7,8,9};
2 a.erase(a.begin(), a.begin()+3);

Para remover o último elemento do vector ,

1 vector<int> a {1,2,3,4,5,6,7,8,9};
2 a.pop_back();

Para remover o primeiro elemento do vector ,

1 vector<int> a {1,2,3,4,5,6,7,8,9};
2 a.pop_front();

Variar o tamanho do vector

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

1 // create empty vector with 5 positions


2 vector<int> a(5);
3 // redefine vector to have 20 positions
4 a.resize(20);

Criação de matrizes

A criação de matrizes pode ser realizada com o elemento dinâmico vector . Admitamos que
queremos definir uma matriz com nr linhas (rows) and nc colunas (columns).

O exemplo seguinte mostra como criar uma matriz de números inteiros nr × nc,

1 int nr=10, nc=12;


2 // create matrix nr x nc
3 vector<vector<int> > M(nr);
4 // extend size of vector<int> to have nc elements
5 for (auto& v: M) {
6 v.resize(nc);
7 }

Comentários:

• linha 3:
um vector com nr vectores de inteiros é alocado em memória. Os vectores de inteiros possuem
0 elementos.
• linha 5:
ciclo no vector<vector<int>> . Cada elemento v do ciclo é um vector<int> .
• linha 6:
estendemos o tamanho do vector v para que este possua um número de elementos nc

Uma outra forma alternativa e mais compacta de criar a matriz seria a seguinte,

1 int nr=10, nc=12;


2 // create matrix nr x nc
3 vector<vector<int> > M( nr, vector<int>(nc) );

Podemos ainda definir a matriz inicializando directamente os seus elementos. No exemplo seguinte
procedemos à criação de uma matriz de números inteiros de 3 × 4,

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

1 vector<vector<int> > M = {
2 {1,2,3},
3 {10,11,12},
4 {20,21,22},
5 {30,31,32}
6 };

Os elementos da matriz podem ser acedidos com o operador [] . Por exemplo para termos acesso
ao elemnto [1][2] (1a linha, 2a coluna),

1 int nr=10, nc=12;


2 // create matrix nr x nc
3 vector<vector<int> > M( nr, vector<int>(nc) );
4 M[1][2] = 5;

Números complexos em C++

Um número complexo possui a seguinte representação matemática,

z = a + bi = r eiθ

onde:

• r = a2 + b2
• θ = atan(b/a)

Na biblioteca STL da linguagem C++ existe a classe complex que permite operações com este
tipo de números.
No exemplo seguinte procedemos a operações sobre números complexos.

1 #include <complex>
2 #include <iostream>
3 using namespace std;
4

5 int main() {
6

7 // create complex number


8 complex<double> z1(1,2), z2(3,4);
9

10 // add numbers

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

11 auto z3 = z1 + z2;
12 cout << z3 << endl;
13

14 // get conjugate
15 complex<double> z3_conjugate = conj(z3);
16

17 // get norm
18 double z3_norm = norm(z3);
19

20 // access to real amd imaginary part


21 double z3_real = z3.real();
22 double z3_imag = z3.imag();
23

24 // exponential
25 complex<double> exponential =exp(z3);
26 cout<<"exponential of z3 = "<<exponential<<'\n';
27

28 // make a complex from polar representation (magnitude and phase)


29 double magnitude=2.0,phase=45;
30 cout<<"magnitude = "<< magnitude << " phase = " << phase<< "
,→ degrees\n";
31 complex<double> z4 = polar(magnitude,(M_PI/180.0)*phase);
32 cout<<"z4 = "<< z4 <<'\n';
33

34 }

A partir da revisão de 2014 do C++ (c++14) é possível escrever simbolicamente um número com-
plexo,

1 #include <complex>
2 #include <iostream>
3 using namespace std;
4 using namespace std::literals;
5

6 int main() {
7 std::complex<double> z = 1.5 + 4.2i;
8 }

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

map

Um mapa ( map ) é um container STL que armazena pares key/value . É chamado um


container associativo e é o equivalente ao dicionário em python.

Para usarmos o container map terrmos que incluir a sua declaração,

1 #include <map>
2 using namespace std;

Criação de um mapa

Criemos de seguida um mapa com uma key do tipo inteiro e um value do tipo double ,

1 // create map and initialize it


2 map<int,double> mapa = { {1,2.345}, {10, 1.23}, {2, 0.23445} };
3

4 // create map and set elements


5 map<int,double> mapa;
6 mapa[1] = 2.345;
7 mapa[10] = 1.23;
8 mapa[2] = 0.23445;
9 mapa.insert({6, 4.56});

Aceder aos elementos do maps e ciclo

Para acedermos aos elementos do mapa, há várias formas que mostram de seguida no exemplo.

2 // auto range for (over map elements)


3 for (auto e: mapa) {
4 cout << e.first << " " << e.second << endl;
5 }
6 cout << "elemento key=2 " << mapa[2] << endl;
7 cout << "elemento key=10 " << mapa.at(10) << endl;

Usando o iterador do mapa,

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

1 // create map
2 map<int,double> mapa = { {1,2.345}, {10, 1.23}, {2, 0.23445} };
3

4 // iterator to map begin


5 auto it = mapa.begin();
6

7 // loop on map elements


8 while (it != mapa.end()) {
9 cout << "[" << it->first << ", " << it->second << "]\n";
10 it++;
11 }
12 }

E ainda com o iterador, desta vez com um for ,

1 // create map
2 map<int,double> mapa = { {1,2.345}, {10, 1.23}, {2, 0.23445} };
3

4 for (auto it = mapa.begin(); it != mapa.end(); it++) {


5 cout<<"["<< it->first << ", " << it->second <<"]"<< endl;
6 }
7 cout << endl;

A partir da revisão de 2017 (c++17),

1 // create map
2 map<int,double> mapa = { {1,2.345}, {10, 1.23}, {2, 0.23445} };
3

4 for (auto [key, value] : mapa) {


5 cout << "[" << key << ", " << value << "]\n";
6 }
7 cout << endl;

Funções várias

Para sabermos o número de elementos do mapa,

1 map<int,double> mapa = { {1,2.345}, {10, 1.23}, {2, 0.23445} };


2 mapa.size();
3 }

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

Para apagarmos o conteúdo do mapa,

1 map<int,double> mapa = { {1,2.345}, {10, 1.23}, {2, 0.23445} };


2 mapa.clear();
3 }

c++20

A partir da revisão de 2020 do C++ (c++20) há a possibilidade de fazer o merge de dois mapas e de
testar se uma determinada key existe.

Testar se o mapa contém uma dada key ,

1 map<int,double> mapa = { {1,2.345}, {10, 1.23}, {2, 0.23445} };


2 for (int x: {1 ,2, 3, 4, 5, 6, 7, 8, 9}) {
3 if (mapa.contains(x)) {
4 cout << x << ": found" << endl;
5 } else {
6 cout << x << ": not found" << endl;
7 }
8 }

Testar se uma determinada key existe,

1 map<int,double> mapa1 = { {1,2.345}, {10, 1.23}, {2, 0.23445} };


2 map<int,double> mapa2 = { {3,4.345}, {20, 3.23}, {5, 0.445} };
3 mapa1.merge(mapa2);
4 }

Utilização de algoritmos em vector

Nos exemplos que se seguem deve proceder à inclusão do header <algorithm> .

Imprimir um vector

O método std::for_each permite percorrer os elementos do vector. Podemos aproveitar isso


para fazer a impressão do vector evitando assim a realização do ciclo. Vamos proceder à criação de
vector de números aleatórios entre 0 e 1 e imprimi-lo.

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

2 // create random numbers


3 vector<double> vr(20);
4 for (double& e: vr) {
5 e = rand()/(double)RAND_MAX; // [0,1]
6 }
7

8 // print vector
9 cout << "vector vr: " <<;
10 std::for_each(vr.begin(), vr.end(), [](double x){ cout << x << " "; });
11 cout << endl;

Copiar um vector

A cópia de um vector para outro, impôe que criemos primeiramente o vector destino com o mesmo
número elementos que pretendemos copiar. No exemplo que se segue, procedemos à cópia integral
do vector de números aleatórios num outro,

2 vector<double> vc(vr.size());
3 std::copy(vr.begin(), vr.end(), vc.begin());

Podemos também realizar uma cópia condicional. Usaremos o método std::copy_if que faz
uso de uma função predicado cujo retorno é um booleano. No exemplo que se segue faremos uma
cópia dos elementos superiores a 0.5,

2 vector<double> vc(vr.size());
3 std::copy_if(vr.begin(), vr.end(), vc.begin() [](double x){return x>0.5;
,→ });

Ordenar um vector

Um vector pode ser ordenado de forma ascendente,

2 std::sort(vr.begin(), vr.end());

ou descendente,

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

2 std::sort(vr.begin(), vr.end(), std::greater<double>());

Soma dos elementos de um vector

Para obtermos a soma dos elementos de um vector, usarmeos uma variável externa sum que
acumulará a soma dos elementos.

2 double sum=0;
3 for_each(vr.begin(), vr.end(), [&sum](double x){ sum += x; });
4 cout << "sum=" << sum << endl;

Transformar um vector

De seguida modificaremos os elementos de um vector de acordo com uma função (neste caso o
logaritmo) e colocaremos noutro vector,

2 vector<double> vt(vr.size());
3 std::transform(vr.begin(), vr.end(), vt.begin(), [](double x) {return
,→ log(x);});

O método transform pode também ser usado para fazer a soma de dois vectores. Neste
exemplo faremos a som de dois vectores de números aleatórios.

2 // create random numbers vector's


3 vector<double> vr1(20), vr2(20);
4 for (double& e: vr1) {
5 e = rand()/(double)RAND_MAX; // [0,1]
6 }
7 for (double& e: vr2) {
8 e = rand()/(double)RAND_MAX; // [0,1]
9 }
10

11 // add both vectors


12 vector<double> result(vr1.size());
13 std::transform(vr1.begin(), vr1.end(), vr2.begin(), result.begin(),
,→ [](double x, double y) {return x+y;});

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

Programação em C++: classes e objectos

Estruturas

Tal como vimos anteriormente, usámos a declaração struct para realizar uma estrutura de
elementos de diferentes tipos que contenha as características do objecto planeta. Uma particulari-
dade da estrutura é que todos os seus elementos são public , ou seja, podem ser acedidos do
exterior; dizendo acedidos, significa que podem ser modificados!
Por exemplo, para modificarmos a massa do pleneta faríamos,

2 // create planet
3 planet mars = {"mars", 6.4171E23, 3389.5E3};
4

5 // print its contents


6 mars.print();
7

8 // modify its mass and print it again


9 mars.mass = 2.3456E12;
10 mars.print();
11 }

Note o uso do operador . para aceder aos elementos da estrutura planet .


constructor
Consultando a declaração da estrutura planet concluimos que o objecto possui três caracterís-
ticas: nome, massa, raio. O que acontece quando declaramos o objecto mars e o inicializamos?
Há uma mão invisível por trás que coloca os valores nas respectivas variáveis. Trata-se de uma
função especial que constrói o objecto e que se designa como constructor . Esta função tem
a particularidade de possuir o nome da estrutura ( planet ) e de não possuir identificação de
retorno (nada, nem void ou outra tipo qualquer).
Vamos de seguida alterar a declaração da estrutura de forma a que esta possua esta função particular
e vamos de seguida chamá-la explicitamente no programa principal,

1 #include <iostream>
2 #include <string>
3 using namespace std;
4

5 struct planet {
6 string nome; // planet name

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

7 double mass; // planet mass (kg)


8 double radius; // planet mean radius (m)
9 double distance; // planet distance to sun (m)
10

11 // -------------------------------------------- functions
12

13 // constructor
14 planet() : nome(""), mass(0.), radius(0) {;}
15 planet(string s, double m, double r) : nome(s), mass(m), radius(r) {;}
16

17 // print
18 void print() {
19 cout << "nome=" << nome << " | mass=" << mass << endl;
20 };
21 };

Criação dos objectos planet mars e mars2, usando as funções constructor ,

1 #include "planet.h"
2 int main() {
3

4 // build empty planet


5 planet mars;
6

7 // build mars planet


8 planet mars2("mars", 6.4171E23, 3389.5E3);
9

10 }

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

ROOT

Documentação

ROOT, é uma biblioteca de C++ gratuita desenvolvida pelo Laboratório Europeu de Física de Partí-
culas (CERN). Possui imensos recursos disponíveis como por exemplo classes para representação
gráfica e tratamento de dados. Para mais detalhes sobre ROOT consulte o seu site.

Figura 0.2: Sítio web do ROOT

No quadro do curso de Física Computacional iremos usar ROOT para fazer representação gráfica
de dados e funções. Existe um avasta documentação sobre ROOT que pode ser encontrada no
endereço . Em particular, podem ser encontrados muitos exemplos de utilização dos recursos de
ROOT sob a forma de tutoriais que podem ser encontrados no endereço .x

Os objectos que ROOT que iremos utilizar no curso são:

• representação gráfica de pontos


Introdução aos objectos gráficos em ROOT
classe TGraph
classe TAttMarker
• representação de dados organizados em intervalos de valores de uma dada variável (1D) ou
variáveis (2D,...)
Introdução aos histogramas em ROOT
classe TH1
classe TH2D
• representação de funções
classe TF1

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

Gráficos: representação de pontos

Vamos de seguida usar o objecto TGraph de ROOT para fazermos a representação gráfica de
pontos. No exemplo que se segue realizamos as seguintes ações:
• geram-se 20 números aleatórios entre 0 e 1
• abre-se uma tela ( TCanvas )
• constrói-se um objecto TGraph e adicionam-se de seguida os 20 pontos
• realiza-se um primeiro gráfico com símbolos quadrados (símbolo 25) nos pontos e estes
ligados por linhas rectas
• salva-se o gráfico num ficheiro .png
• um duplo click na janela gráfica fará avançar o programa ( WaitPrimitive )
• a chamada de gSystem->ProcessEvents() activa o display do gráfico
• segue-se um segundo gráfico

2 #include <vector>
3 #include <iostream>
4 #include <algorithm>
5 using namespace std;
6

7 #include <cstdlib> // rand()


8

9 #include "printv.h"
10

11 // root includes
12

13 #include "TCanvas.h"
14 #include "TApplication.h"
15 #include "TGraph.h"
16 #include "TSystem.h"
17

18 int main() {
19

20 ////////////////////// random numbers


21

22 // create 20 random numbers between [0,1]


23

24 vector<double> vr(100);
25 for (auto& e: vr) {
26 e = rand()/(double)RAND_MAX;
27 }
28 printv(vr,"vr");

Fernando Barão Guia introdutório


Fis Computacional/IST (2021-22, P3) 12 de Março 2022

29

30 // create a vector v2 containing the set of random numbers and mean=0


31

32 auto v2=vr;
33 double sum=0.;
34 for_each(vr.begin(), vr.end(),[&sum](double x){ sum +=x; });
35 double mean = sum/vr.size();
36 transform(v2.begin(), v2.end(), v2.begin(), [mean](double x){return
,→ x-mean;});
37

38 ///////////////////// drawing
39

40 // draw
41 // - TCanvas
42 // - TGraph
43 // - TApplication
44

45

46 TApplication A("A",nullptr,nullptr);
47 TCanvas canvas("canvas", "O meu primeiro canvas", 0, 0, 1200, 800);
48

49 // 1st graph
50

51 TGraph g;
52 int i=0;
53 for (auto e: vr) {
54 g.AddPoint(i,e);
55 i++;
56 }
57 g.SetMarkerStyle(90);
58 g.SetMarkerSize(1.5);
59 g.SetMarkerColor(kRed+2);
60 g.SetLineColor(kBlue+1);
61 g.SetLineWidth(4);
62 g.Draw("APL"); // axis drawn
63 g.SetTitle("random numbers; index; amplitude");
64 canvas.Update();
65 canvas.SaveAs("FIG_random.png");
66 canvas.WaitPrimitive();
67 gSystem->ProcessEvents();
68

69 // 2nd graph

Guia introdutório Fernando Barão


12 de Março 2022 Fis Computacional/IST (2021-22, P3)

70

71 TGraph g2;
72 i=0;
73 for (auto e: v2) {
74 g2.AddPoint(i,e);
75 i++;
76 }
77 g2.SetMarkerStyle(90);
78 g2.SetMarkerSize(1.5);
79 g2.SetMarkerColor(kRed+2);
80 g2.SetLineColor(kBlue+1);
81 g2.SetLineWidth(4);
82 g2.Draw("APL"); // axis drawn
83 g2.SetTitle("random numbers-mean; index; amplitude");
84 canvas.Update();
85 canvas.SaveAs("FIG_random-mean.png");
86 canvas.WaitPrimitive();
87 gSystem->ProcessEvents();
88

89 }

Figura 0.3: TGraph: 20 pontos aleatórios em função da ordem de tiragem

Fernando Barão Guia introdutório

Você também pode gostar