Há alguns semestres eu leciono matérias como Programação III ou Programação Aplicada de Computadores, que focam em programação orientada a objetos e incluem em seus programas linguagens como Java e C++. Ensinar Java é bem tranquilo, visto que programo nesta linguagem desde 1999, sempre fiz parte de JUGs (mesmo durante o doutorado), escrevi artigos para revistas sobre Java, enfim… me mantive atualizado.
Já em C++ minha experiência era bem limitada em comparação. Antes de começar a lecionar usando esta linguagem, só a tinha utilizado uma vez durante o meu curso de graduação, na disciplina Linguagens de Programação com o prof. Flávio Varejão. Na época, o currículo da Ciência da Computação não incluía uma disciplina específica para Programação OO.
O resultado é que fui aprendendo bastante sobre a linguagem ao longo destes quase 3 anos como professor do DI/Ufes. Criei até um utilitário C++ com funções de tokenização (tipo java.lang.String.split()), formatação de datas (tipo java.text.SimpleDateFormat) e de números (tipo java.text.NumberFormat).
Este semestre, no entanto, resolvi fazer diferente das últimas vezes que dei essa matéria e implementei o trabalho prático não só em Java, mas também em C++. Descobri que tinha muita coisa necessária no processo de “tradução” de um programinha simples em Java para C++ que eu estava ainda por descobrir. Montei, então, esse “FAQ” com algumas dicas para quem segue nessa mesma estrada do Java para o C++. Espero que ajude!
Como faço pra verificar que uma string contém uma data válida (ParseException em Java)?
Partindo do utilitário DateUtils, poderíamos adicionar um novo método para fazer isso:
[cc_cpp]
bool validDate(const string& str, const string& formato) {
struct tm tm;
return strptime(str.c_str(), formato.c_str(), &tm);
}
[/cc_cpp]
Como faço pra verificar que uma string contém um número inteiro válido (ParseException em Java)?
Para conter essa função criei um novo utilitário, StringUtils:
[cc_cpp]
bool isNumber(string& s) {
if (s.empty()) return false;
for (int i = 0; i < s.size(); i++)
if (! isdigit(s.at(i))) return false;
return true;
}
[/cc_cpp]
A função poderia ser adaptada para aceitar também números reais, aceitando também o separador (vírgula ou ponto).
Como fazer java.lang.String.trim() em C++?
Essa resposta eu achei num post do StackOverflow e, adaptando o código, adicionei ao utilitário StringUtils as seguintes funções (e includes):
[cc_cpp]
#include
#include
#include
#include
#include
string& ltrim(string &s) {
s.erase(s.begin(), find_if(s.begin(), s.end(), not1(ptr_fun
return s;
}
string& rtrim(string &s) {
s.erase(find_if(s.rbegin(), s.rend(), not1(ptr_fun
return s;
}
string& trim(string& s) {
return ltrim(rtrim(s));
}
[/cc_cpp]
Como faço comparação de strings com Collator (para considerar letras com acentos)?
Essa eu recebi por e-mail do prof. João Paulo Andrade Almeida, que por sua vez recebeu de seu aluno de Programação III, Eduardo Dalapicola. Adicionei mais uma para o StringUtils:
[cc_cpp] Na biblioteca [cci_cpp] Um exemplo concreto para ficar mais fácil de entender, digamos que exista uma classe [cci_cpp]Emprestimo[/cci_cpp], com métodos para obter a data do empréstimo e o nome do tomador do empréstimo. Caso eu queira ordenar um [cci_cpp]vector [cc_cpp] E a função de comparação seria definida assim: [cc_cpp] Notem que usei aqui a função de comparação de strings com collator, apresentada anteriormente. Além disso, repare que quando o mesmo ponteiro é passado nos dois argumentos, ele retorna falso (por conta de [cci_cpp](esq != dir)[/cci_cpp]) e isso é fundamental, se não a ordenação fica toda bagunçada (ao menos na minha experiência). As classes-template [cci_cpp]set[/cci_cpp] e [cci_cpp]map[/cci_cpp] (presentes nas bibliotecas homônimas) já possuem ordenação implementada. Ao declarar a coleção, você especifica na configuração do template não só qual o elemento que será armazenado mas também uma classe comparadora para aquele elemento. Note que desta vez é uma classe comparadora e não uma função como na ordenação com [cci_cpp]sort()[/cci_cpp] descrita anteriormente. Vamos ao exemplo, e desta vez vou usar um bem complexo pra exemplificar conjuntos e mapas simultaneamente! Digamos que você queira mapear pessoas à produções artísticas (filmes, livros, séries) em que ela esteja envolvida. Temos então um mapa da classe [cci_cpp]Pessoa[/cci_cpp] para um conjunto da classe [cci_cpp]Midia[/cci_cpp], sendo que o mapa de pessoas deve ser ordenado e o conjunto de mídias também! Declarei a coleção da seguinte forma: [cc_cpp] E as classes de comparação: [cc_cpp] class PessoaComparator { Enfim, a implementação da sobrecarga do operador [cci_cpp]()[/cci_cpp]: [cc_cpp] bool PessoaComparator::operator()(const Pessoa* esq, const Pessoa* dir) const { Note que precisamos novamente do [cci_cpp](esq != dir)[/cci_cpp] (explicada anteriormente), usei também a comparação de strings com collator (apresentada anteriormente) e que as classes comparadoras devem ter acesso às propriedades (normalmente privativas) das classes que elas comparam (facilmente resolvido com um [cci_cpp]friend class PessoaComparator;[/cci_cpp] na classe [cci_cpp]Pessoa[/cci_cpp], por exemplo. Basta usar o método [cci_cpp]good()[/cci_cpp] da classe [cci_cpp]ifstream[/cci_cpp], assim: [cc_cpp] Obviamente a classe [cci_cpp]FileNotFoundException[/cci_cpp] deve ser definida, ela não existe na API do C++. Em Java, dado um mapa [cci_java]m[/cci_java], o método [cci_java]m.get(chave)[/cci_java] retorna nulo se a chave não está presente no mapa. Em C++, isso lança uma exceção. O que você precisa fazer é verificar se o mapa possui aquela chave antes de tentar obter seu valor: [cc_cpp] Outra diferença é que, em Java, adicionar um novo par [cci_java]m.add(chave, valor)[/cci_java] para uma chave que já existe substitui o antigo valor associado à chave. Já em C++ isso adiciona um novo valor à chave (se ela tinha [cci_cpp]count() == 1[/cci_cpp] antes, passa a ter [cci_cpp]count() == 2[/cci_cpp])! Para substituir um valor, podemos fazer assim: [cc_cpp] O exemplo acima foi retirado de um código que conta quantas vezes um gênero aparece em um conjunto de mídias. O comando [cci_cpp]find()[/cci_cpp] recebe um [cci_cpp]Genero*[/cci_cpp] e retorna um iterador do mapa que aponta para o primeiro par que possua essa chave. Em seguida verificamos se havia de fato um par (caso contrário ele aponta para o marcador [cci_cpp]end()[/cci_cpp] do mapa) e incrementamos o valor associado àquela chave (manipulando o atributo [cci_cpp]->second[/cci_cpp]). Segue código de exemplo que me foi enviado pelo prof. João Paulo: [cc_cpp]
bool stringCompare(string s1, string s2) {
const collate
transform(s1.begin(), s1.end(), s1.begin(), ::tolower);
transform(s2.begin(), s2.end(), s2.begin(), ::tolower);
const char* pb1 = s1.data();
const char* pb2 = s2.data();
return (col.compare(pb1, pb1 + s1.size(), pb2, pb2 + s2.size()) < 0);
}
[/cc_cpp]
Como faço para ordenar um vetor de objetos, tipo java.util.Collections.sort()?
sort(emprestimos.begin(), emprestimos.end(), comparaEmprestimos);
[/cc_cpp]
bool comparaEmprestimos(const Emprestimo* esq, const Emprestimo* dir) {
int diff = difftime(esq->getDataEmprestimo(), dir->getDataEmprestimo());
if (diff != 0) return (esq != dir) && (diff >= 0);
return stringCompare(esq->getTomador(), dir->getTomador());
}
[/cc_cpp]Como faço coleções ordenadas, tipo java.util.TreeSet e java.util.TreeMap?
map
[/cc_cpp]
class MidiaComparator {
public:
bool operator()(const Midia* esq, const Midia* dir) const;
};
public:
bool operator()(const Pessoa* esq, const Pessoa* dir) const;
};
[/cc_cpp]
bool MidiaComparator::operator()(const Midia* esq, const Midia* dir) const {
return (esq != dir) && stringCompare(esq->nome, dir->nome);
}
return (esq != dir) && stringCompare(esq->nome, dir->nome);
}
[/cc_cpp]Como faço pra lançar uma exceção tipo java.io.FileNotFoundException quando um arquivo que quero ler não existe?
ifstream in(nomeArquivo);
if (! in.good()) {
in.close();
throw FileNotFoundException();
}
// Restante do código de leitura, normal…
[/cc_cpp]Como faço para verificar se um elemento existe num mapa? Ou para substitui-lo?
if (mapa.count(chave) == 0) { /* Faça alguma coisa. */ }
[/cc_cpp]
map
if (itGen != qtdGen.end()) itGen->second += 1;
[/cc_cpp]Como faço pra usar Locales?
#include
#include
#include
using namespace std;
int main()
{
// obtem o locale
locale brasilLocale(“pt_BR.UTF-8”);
int n; // inteiro que queremos ler
// aplica o locale brasileiro à entrada padrão
// para que esta passe a ser interpretada como queremos
cin.imbue(brasilLocale);
// teste de leitura da entrada padrão
cin >> n; // permitirá entrada como “5.001” = cinco mil e um
// mostra o resultado
cout << n;
}
[/cc_cpp]