Primeiras impressões de Qt

Hoje assisti a primeira aula de um curso de introdução a Qt, oferecido pela Nokia no Centro de Informática da UFPE. A intenção é que, após oito aulas de quatro horas, estejamos com um jogo publicado na Ovi Store. A proposta é muito instigante.

Foto Aula Qt
Primeiros minutos do curso introdutório de Qt

Thiago Lacerda, do Instituto Nokia de Tecnologia (INdT), iniciou a aula dando uma visão geral do Qt e seu SDK. Logo após, colocamos a mão na massa e codificamos no QtCreator (IDE do Qt). Foram duas aplicações simples para console. Na primeira, criamos uma classe (derivada de QObject) com três timers, que imprimia o ID de cada timer ao expirar seu tempo. Para criar os timers, não precisamos de nenhum objeto membro. Bastou chamar startTimer(int msec), herdado de QObject, para criar em tempo de execução quantos timers quiséssemos. E para imprimir o ID na tela, bastou dar overload no método timerEvent (também de QObject), e colocar o código de impressão do ID do evento.

Foi meu primeiro contato com o QtCreator. A sensação de ter uma IDE que digita praticamente tudo para você é muito bom. É parecido com o Eclipse, só que o QtCreator por algum motivo parece ser mais "confortável". Talvez seja o fato de ter uma poluição menor de opções nos menus. É mais fácil de encontrar as coisas.

Minha única queixa foi em relação às mensagens de erro. Várias vezes erros meus levaram a mensagens de erro apontando para partes internas do Qt. Erros devido à falta de #includes também não eram claros. Se não me falha a memória, em certo momento, tudo que eu precisava era um "QTimerEvent undefined", pois não havia incluído <QTimerEvent>. Mas o que o Qt imprimia na saída de erros, não tinha muito a ver com isso.

Mas isso não me desencorajou a utilizar o Qt. A extensa e bem feita documentação online, IDE com excelente interface, assim como a enorme base de usuários (é fácil encontrar soluções de problemas de Qt no Google), dá a impressão que desenvolver em Qt deve ser muito tranquilo.

Para quem nunca ouviu falar, Qt é basicamente uma biblioteca enorme, em C++, feita para ser compilada em diversas plataformas: De Windows, Mac, Linux até iOS, Android, Symbian.  Além da biblioteca, também existe o kit de desenvolvimento, constituído de IDE e outras facilidades, como UI designer tipo drag-and-drop e simuladores para plataformas móveis.

Bom, estas foram as minhas primeiras impressões de Qt. A próxima aula é sobre QML, que segundo Daker Fernandes (INdT), é a mais importante do curso. Até lá, então!

Eficiência em C++: passar um objeto por referência-para-const

Sempre que definimos uma função como:

void check(User u);

e a chamamos

User u1;

check(u1);


o objeto User é passado por valor, como também acontece em C.

Assim, o que temos dentro da função check é uma cópia do objeto u1, o que significa que foi preciso chamar o copy-constructor de User para criar u a partir de u1. Além disso, ao terminar a função check, também é preciso chamar o destrutor de User, já que u saiu de escopo.

Há como fazer o mesmo gastando menos?
Se você quer apenas capturar uma informação do usuário u1 (não quer modificá-lo), você pode economizar estas chamadas ao construtor e destrutor de User (que podem ser mais custosas do que parece, por exemplo, se esta classe tiver muitos membros, for derivada de outra classe (neste caso chamaria também o construtor da classe base), entre outros detalhes).

Para tornar seu código mais eficiente, basta passar o objeto User como referência para const.
A assinatura da função fica:

void check(const User& u);

A chamada da função continuaria a mesma:

User u1;

check(u1);

Qual a consequência disto?

Em primeiro lugar, não será chamado construtor algum. É como se estivesse sendo passado um ponteiro para a função check, só que sendo uma referência (em vez de um ponteiro), você não dá à função check o poder de modificar o objeto original, assim como quando se passa por valor - é criada uma cópia dentro da função. A segunda vantagem é que o código da função não muda. A utilização da referência é igual a como se tivesse sido passada por valor, como se u fosse um objeto. Isto é bom, pois o ganho com eficiẽncia não traz complexidade ao código e você pode fazer um upgrade na performance do seu programa alterando apenas a assinatura da função.

'Converting Constructor' e 'Explicit' em C++

Em C++, se você possuir uma classe MeuInteiro e criar para ela um construtor que recebe apenas um objeto (por exemplo, um int) como argumento, ao fazer um assignment de um objeto int para um MeuInteiro, este construtor será executado.

Exemplo:

#include ⟨cstdio⟩

class MeuInteiro {
public:
        MeuInteiro(int);
};

MeuInteiro::MeuInteiro(int _x)
{
        printf("Construtor MeuInteiro(%d)\n", _x);
}

int main(void)
{ 
        MeuInteiro m = 10;
        return 0;
}

A saída deste programa será:


Construtor MeuInteiro(10) 

Esta conversão de tipo também acontece ao passarmos um inteiro como argumento para funções que recebam o objeto MeuInteiro. Por exemplo, se adicionarmos a função func que recebe um MeuInteiro:

void func(MeuInteiro _m)
{
        printf("func(MeuInteiro);\n");
}


E a chamarmos no main passando um inteiro como argumento:

int main(void)
{ 
        MeuInteiro m = 10;
        func(15);
        return 0;
}


A saída será:
Construtor MeuInteiro(10)
Construtor MeuInteiro(15)
func(MeuInteiro);

No entanto, se você não quiser que essa conversão implícita aconteça, você pode acrescentar a keyword explicit ao construtor. Desta forma, haverá erro de compilação, pois não é possível converter  int para MeuInteiro.

Aqui vai o exemplo, que demonstra o parágrafo anterior, e sua saída:

convconstr.cpp
#include ⟨cstdio⟩

class MeuInteiro {
public:
        explicit MeuInteiro(int);
};

MeuInteiro::MeuInteiro(int _x)
{
        printf("Construtor MeuInteiro(%d)\n", _x);
}

void func(MeuInteiro _m)
{
        printf("func(MeuInteiro);\n");
}

int main(void)
{ 
        MeuInteiro m = 10;
        func(15);
        return 0;
}

Saída:
g++ -Wall convconstr.cpp -o convconstr

convconstr.cpp: In function ‘int main()’:
convconstr.cpp:20:17: error: conversion from ‘int’ to non-scalar type ‘MeuInteiro’ requested
convconstr.cpp:21:9: error: conversion from ‘int’ to non-scalar type ‘MeuInteiro’ requested

Estas chamadas implícitas também acontecem com o copy-constructor. Mas tornar o copy-constructor explicit provavelmente será má ideia. Uma classe com o copy-constructor explícito significa que não é possível passar objetos por valor a nenhuma função, ou realizar inicializações utilizando o operador =. Aqui há uma thread do cplusplus a este respeito: http://www.cplusplus.com/forum/unices/62973/

Série de posts sobre C++ Eficaz

Embora minha experiência nesta linguagem seja quase nula, estou há quase um mês desenvolvendo em C++. Não tive muito tempo de ler sobre as peculiaridades da linguagem (que são muitas) e estou aprendendo à medida que cometo erros.

O problema é que o sistema está crescendo e os erros estão ficando cada vez mais difíceis de resolver. Sem contar que tenho a impressão que o código está inconsistente, com classes que fazem praticamente a mesma coisa, com padrões diferentes, já que não sigo padrão algum.

Por isso procurei por um livro que pudesse me guiar em como ser correto e eficaz em C++, sem entrar em detalhes sobre a sintaxe, que se encontra em qualquer site, e sim detalhes sobre implementação. O que se deve e o que não se deve fazer. Eis que encontrei o Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition) por Scott Meyers.



Então, seguindo a dica de Confúcio, resolvi reproduzir aqui neste blog o que estou aprendendo no livro.

"Ouço e esqueço. Vejo e me lembro. Faço e entendo." - Confúcio.


Creio que a leitura por si só não é suficiente para mim. Entendo o que está sendo dito, mas não memorizo. Quando você precisa explicar para alguém, aí sim, é um sinal de que você aprendeu.

Então vamos lá.