Pular para o conteúdo

Compilando programas em C++

Neste capítulo, vamos aprender a compilar e executar programas com um único arquivo e projetos na linguagem C++.

No terminal, abra a pasta c_cpp_projects que criamos anteriormente para guardar nossos projetos.

Fedora, WSL ou Mint
cd ~/dev/c_cpp_projects

Crie um diretório chamado binary_tree, entre nele e abra-o no Visual Studio Code.

Fedora, WSL ou Mint
mkdir -p binary_tree
cd binary_tree
code .

Lembre-se de ativar o perfil C/C++ que criamos no capítulo Configurando o VSCode.

Crie um arquivo chamado main.cpp e adicione o seguinte código.

main.cpp
#include <iostream>
using namespace std;
int main()
{
cout << "Hello, World!" << endl;
return 0;
}

Para compilar o código, utilizamos o comando clang++, em vez de apenas clang. A estrutura do comando é a mesma. Antes de executá-lo, vamos criar uma pasta chamada build para guardar o arquivo binário gerado.

Fedora, WSL ou Mint
mkdir -p build
clang++ main.cpp -o build/binary_tree

Para executar o programa, basta chamar o arquivo binário gerado.

Fedora, WSL ou Mint
./build/binary_tree
Terminal com os comandos para compilar e executar o programa "Binary Tree", cujo resultado é "Hello, World!"
Compilação e execução do programa "Binary Tree".

Vamos editar este projeto para de fato representar uma árvore binária. Crie duas pastas dentro do projeto: tree e node.

Fedora, WSL ou Mint
mkdir -p tree node
  • Dentro de tree, crie um arquivo tree.cpp e um tree.hpp.
  • Dentro de node, crie um arquivo node.cpp e um node.hpp.
Fedora, WSL ou Mint
touch tree/tree.cpp tree/tree.hpp node/node.cpp node/node.hpp
  • Na raiz do projeto, crie os arquivos main.cpp, .clang-format e .clang-tidy.
Fedora, WSL ou Mint
touch main.cpp .clang-format .clang-tidy
  • Use os snippets para preencher o conteúdo dos arquivos de formatação e de linting.

A estrutura do projeto deve ficar da seguinte forma.

  • Directorynode
    • node.cpp
    • node.hpp
  • Directorytree
    • tree.cpp
    • tree.hpp
  • .clang-format
  • .clang-tidy
  • main.cpp

Copie o conteúdo de todos os arquivos abaixo para os respectivos arquivos criados. Não se esqueça de salvar cada um deles.

main.cpp
#include "tree/tree.hpp"
#include <iostream>
using namespace std;
int main() {
Tree tree;
tree.insert(5);
tree.insert(3);
tree.insert(8);
tree.insert(1);
tree.insert(4);
tree.insert(7);
tree.insert(9);
tree.print();
cout << endl;
return 0;
}
node/node.hpp
#ifndef __NODE_HPP__
#define __NODE_HPP__
class Node {
private:
int data;
Node *left;
Node *right;
public:
Node(int data);
~Node() {}
int getData();
Node *getLeft();
Node *getRight();
void setData(int data);
void setLeft(Node *left);
void setRight(Node *right);
};
#endif // __NODE_HPP__
node/node.cpp
#include "node.hpp"
Node::Node(int data) {
this->data = data;
left = nullptr;
right = nullptr;
}
int Node::getData() {
return data;
}
Node *Node::getLeft() {
return left;
}
Node *Node::getRight() {
return right;
}
void Node::setData(int data) {
this->data = data;
}
void Node::setLeft(Node *left) {
this->left = left;
}
void Node::setRight(Node *right) {
this->right = right;
}
tree/tree.hpp
#ifndef __TREE_HPP__
#define __TREE_HPP__
#include "../node/node.hpp"
class Tree {
private:
Node *root;
void insert(Node *node, int data);
void print(Node *node);
public:
Tree();
~Tree();
void insert(int data);
void print();
};
#endif // __TREE_HPP__
tree/tree.cpp
#include "tree.hpp"
#include <iostream>
using namespace std;
Tree::Tree() {
root = nullptr;
}
Tree::~Tree() {
Node *current = root;
while (current != nullptr) {
Node *temp = current;
current = current->getRight();
delete temp;
}
}
void Tree::insert(int data) {
if (root == nullptr) {
root = new Node(data);
} else {
insert(root, data);
}
}
void Tree::insert(Node *node, int data) {
if (data < node->getData()) {
if (node->getLeft() == nullptr) {
node->setLeft(new Node(data));
} else {
insert(node->getLeft(), data);
}
} else {
if (node->getRight() == nullptr) {
node->setRight(new Node(data));
} else {
insert(node->getRight(), data);
}
}
}
void Tree::print() {
print(root);
}
void Tree::print(Node *node) {
if (node != nullptr) {
print(node->getLeft());
cout << node->getData() << " ";
print(node->getRight());
}
}

Antes de compilar o projeto, vamos inicializar o controle de versão com o Git.

Crie o arquivo .gitignore na raiz do projeto, e o preencha com o snippet gitignore-c-cpp.

Então, inicialize o repositório Git e faça o primeiro commit.

Fedora, WSL ou Mint
git init
git add .
git commit -m "Initialize project"

Para compilar o projeto, precisamos passar todos os arquivos para o compilador. Fazemos isso usando o comando clang++ da seguinte maneira:

Fedora, WSL ou Mint
clang++ <arquivos.cpp> -I <pastas_com_arquivos.hpp> -o <executável>
  • <arquivos.cpp>: todos os arquivos .cpp do projeto.
  • <pastas_com_arquivos.hpp>: todas as pastas que contêm arquivos .hpp do projeto.
  • <executável>: nome do arquivo binário gerado.

Ou seja, precisamos especificar a localização de todos os arquivos que desejamos incluir e compilar. Dado que nosso projeto tem mais de uma pasta internamente, precisamos especificar a localização de todas elas. Execute o código a seguir.

Fedora, WSL ou Mint
clang++ tree/tree.cpp node/node.cpp main.cpp -I tree -I node -o build/binary_tree

Para executar o programa, basta chamar o arquivo binário gerado.

Fedora, WSL ou Mint
./build/binary_tree

Se tudo estiver correto, você verá os nós da árvore binária sendo impressos no terminal em ordem crescente.

Terminal com os comandos para compilar e executar o projeto "binary_tree", cujo resultado é "1 3 4 5 7 8 9"
Compilação e execução do projeto "binary_tree".

Assim como fizemos para projetos em C, podemos configurar tarefas de compilação para C++ no Visual Studio Code. Se você seguiu os passos recomendados no capítulo Configurando o VSCode, já está configurada uma tarefa de compilação para C++.

Infelizmente, essa tarefa apenas compila arquivos .cpp que estejam na raiz do projeto. Uma vez que nosso projeto tem arquivos em pastas diferentes, precisamos especificar a localização de todos eles. Dessa forma, a tarefa de compilação global não é suficiente para compilar o projeto binary_tree. Precisamos criar um arquivo de tarefa personalizado para isso.

Nós configuramos também alguns snippets para facilitar a construção do arquivo tasks.json. Crie uma pasta chamada .vscode na raiz do projeto e um arquivo chamado tasks.json dentro dela.

Fedora, WSL ou Mint
mkdir -p .vscode
touch .vscode/tasks.json

Garanta que o perfil C/C++ esteja ativo e abra o arquivo tasks.json. Digite nele tasks-json e pressione Enter Enter para preencher o arquivo com o snippet.

Visual Studio Code com o arquivo "tasks.json" aberto, sugerindo o preenchimento com o snippet para construção de tarefas.
Construção do arquivo "tasks.json".

Então, dentro do vetor tasks, digite cpp-build-task e pressione Enter Enter para preencher o arquivo com o snippet.

Visual Studio Code com o arquivo "tasks.json" aberto, sugerindo o preenchimento com o snippet para a tarefa de compilação de C++.
Preenchimento da tarefa de compilação "cpp-build-task".

Observe o campo args do arquivo tasks.json. Ele contém os argumentos que passamos para o comando de compilação. Atualmente, ele recebe todos os arquivos .cpp que estejam na raiz do projeto.

tasks.json
"args": [
"-fcolor-diagnostics",
"-fansi-escape-codes",
"-g",
"${workspaceFolder}/*.cpp",
"-I",
"${workspaceFolder}",
"-o",
"${workspaceFolder}/build/${workspaceFolderBasename}"
],

Precisamos passar, além desses, todos os arquivos .cpp que estão nas pastas tree e node. Para isso, adicionamos uma linha para cada pasta, iniciando com o atalho ${workspaceFolder}, o qual indica a pasta raiz do projeto.

O arquivo tasks.json deve ficar da forma abaixo.

.vscode/tasks.json
{
"version": "2.0.0",
"tasks": [
{
"args": [
"-fcolor-diagnostics",
"-fansi-escape-codes",
"-g",
"${workspaceFolder}/*.cpp",
"${workspaceFolder}/node/*.cpp",
"${workspaceFolder}/tree/*.cpp",
"-I",
"${workspaceFolder}",
"-o",
"${workspaceFolder}/build/${workspaceFolderBasename}"
],
"command": "clang++",
"detail": "Tarefa de compilação de um projeto em C++. Compila todos os arquivos C++ ligados do projeto em um único executável.",
"group": {
"kind": "build"
},
"label": "Clang: build C++ project (Local)",
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": ["$gcc"],
"type": "cppbuild"
}
]
}

Poderemos então usar o atalho Control + Shift + B Control + Shift + B para abrir o painel de tarefas de compilação. Então selecione a Clang: build C++ project (Local).

Visual Studio Code com o painel de tarefas aberto, mostrando a tarefa "Clang: build C++ project (Local)" selecionada.
Compilação do projeto "binary_tree" com a tarefa personalizada.

O Visual Studio Code compilará o projeto e mostrará o resultado no terminal integrado.

Terminal integrado do Visual Studio Code com a saída da compilação do projeto "binary_tree".
Saída da compilação do projeto "binary_tree".