sexta-feira, 7 de setembro de 2012

Pointeiros Inteligentes (smart_pointers) parte 1: unique_ptr

C++ é amado (ou odiado) por muitos pelo controle que dá ao programador. A gerência de memória é feita quase que manualmente no código, ainda não temos gerência automática de memória. A sua implementação está prevista para a versão atual, mas ainda não há suporte na maioria dos compiladores.

Gerência automática de memória é excelente por um lado, mas como o usuário não tem controle sobre quando a limpeza da memória vai ser feita, podemos ter comportamentos estranhos no nosso programa. Em certas aplicações, como as de tempo real, queremos ter garantias sobre o tempo de execução, por isso a gerência manual da memória ainda é relevante.

Porém, como o programado é um ser humano (em geral), ele comete erros. Principalmente quando o projeto começa a ficar muito grande. Então temos as temidos vazamentos de memória. Esse fenômeno acontece quando criamos um objeto com o operador new, mas esquecemos de deletá-lo. Então aquele setor da memória fica ocupado e o programa não consegue mais utilizá-lo.

Outro problema acontece quando múltiplas partes do código compartilham o mesmo objeto contido na memória dinâmica. De quem é a responsabilidade de deletá-lo? um método pode deletar um objeto que ainda está sendo utilizado por outro método.

Para solucionar tudo isso, C++11 contém ponteiros inteligentes, que são estruturas que garantem que os dados da memória serão liberados quando não forem mais necessários.

Nesse artigo, vamos aprender sobre o primeiro tipo de ponteiro inteligente: unique_ptr. Esses tipos de ponteiros garantes que apenas um ponteiro vai conter a referência ao objeto de cada vez. Para que se possa passar o objeto adiante, deve-se "liberá-lo" do ponteiro.

Hora de ver unique_ptr em ação. Vamos primeiro definir um objeto que exibe uma mensagem quando está sendo deletado.
class Data{
public:
 void aMethod(){ cout << "method called" << endl;}
 ~Data(){ cout << "data is being deleted" << endl; }
};
Agora quando o ponteiro sai fora do escopo, o objeto associado é deletado.
{
  unique_ptr<Data> dataPtr { new Data() };
}
Para passar um objeto adiante, o método release deve ser usado. Assim temos a garantia que o objeto só tem um dono. A saída do programa abaixo seria o primeiro ponteiro sendo 0 (porque já liberou o objeto) e o segundo sendo igual ao ponteiro para o objeto.
unique_ptr<Data> dataPtr = unique_ptr<Data>(new Data());
unique_ptr<Data> dataPtr2 {dataPtr.release()};

cout << dataPtr.get() << " and " << dataPtr2.get() << endl;
Ponteiros inteligentes se comportam como ponteiros normais, então é possível chamar métodos, acessar elementos de um vetor, etc. Abaixo um exemplo da chamada de um método.
dataPtr2->aMethod();
Finalmente, uso mais interessante de ponteiros inteligentes é o retorno de objetos criados dentro de funções. O retorno de um ponteiro inteligente garante que os clientes da função tem que usar um ponteiro inteligente para receber os dados.
unique_ptr<int> createOne(){
 return unique_ptr<int>(new int(1));
}

// [...]

unique_ptr<int> one_p = createOne();
cout << *one_p << endl;
O código completo pode ser visto no github, em unique_ptr.cpp.

domingo, 2 de setembro de 2012

Asserções Estáticas (em tempo de compilação) em C++11

Nos próximos posts vamos explorar algumas novidades do novo padrão do C++: o C++11. Vamos começar por asserções estáticas.

Uma asserção é teste que garante a corretude do código. C++ já possuía asserções, contidas na <cassert>, que poderiam ser ativadas só no modo de debug. A vantagem das asserções estáticas é que elas são executadas em tempo de compilação, não acarretando nem prejuízo em performance. Elas podem ser usadas, por exemplo, para garantir certas propriedades estáticas de classes usadas por clientes.

Como exemplo, vamos implementar uma classe que contenha um número. Queremos garantir que somente tipo numéricos vão poder ser usados como parâmetro.
class Function{
template <typename T>
class Number {
  T number_;
public:
  Number(T number) : number_(number) {
    // flags a compilation error if experession is false
    static_assert(std::is_arithmetic<T>::value,
                            "type must be a number"); 
  }
};
Na linha 8 temos o static_assert, que recebe dois parâmetros: a expressão, que deve ser verdadeira, e a mensagem de erro a ser exibida. Caso a expressão seja falsa, o código não vai compilar. Agora se usarmos um tipo int ou float, não teremos nenhum problema de compilação.
class B {

};

int main()
{
  double d;
  int i;
  B b;
  
  Number<double> cd(d); // OK, d is a floating-point number 
  Number<int> ci(i);    // OK, i is Integral
  Number<B> cb(b);    // Not OK, b is a class 
}
Mas se utilizarmos um tipo não-primitivo, teremos o seguinte erro:
static_assert.cpp: In constructor ‘Number<T>::Number(T) [with T = B]’:
static_assert.cpp:27:17:   instantiated from here
static_assert.cpp:11:5: error: static assertion failed: "type must be a number"
O código completo pode ser visto no github, em static_assert.cpp.

sábado, 12 de maio de 2012

Funtores em C++, parte II

Na última parte, aprendemos como utilizar funtores em C++. Hoje vamos ver qual é a sua vantagem. Já que da maneira como aprendemos, eles não diferem muito de ponteiros para funções.

A vantagem do funtor é que ele é um objeto como qualquer outro, então pode ter métodos, variáveis membro, etc. Podemos dessa forma criar "fábricas" de funtores, seja dinamicamente, em tempo de execução, ou estaticamente, através de templates.

Vou demonstrar como utilizar cada uma das duas técnicas para criar uma classe polinômio.

1. Criando funtores estáticos com templates

Para criarmos a fábrica de funtores estaticamente, vamos parametrizar a classe derivada de Function para definir um polinômio de grau dois.
template<int A0, int A1, int A2>
 class Poly3 : public Function
 {
 public:
  double operator ()(double x) const {
   return A0 + x * (A1 + A2 * x);
  }
 };
Para criar uma nova função é só criar uma objeto atribuindo os parâmetros. Por exemplo, para definirmos a função do post anterior (x^2 - 1), podemos fazer assim:
Function* p = new Poly3<-1, 0, 1>();
A desvantagem dessa abordagem é o que os parâmetros de template devem ser integrais, reduzindo então a gama de funções que podemos representar. A vantagem é que temos aqui o mesmo custo que se criássemos uma classe nova para cada função (cada parametragem diferente vai gerar código diferente).

2. Criando funtores dinâmicos

Usaremos a mesma classe base do post anterior, mas dessa vez nosso funtor será um polinômio. Como parâmetro do construtor teremos um vetor de coeficientes; o grau do polinômio será definido pelo tamanho desse vetor.
template <class T>
 class Polynomial : public Function{
  vector<T> a;
 public:
  Polynomial(vector<T> &coefficients) : a(coefficients) { }
  T operator ()(T x) const {
   T total = 0;
   for(int i=0; i < a.size(); ++i)
    total += a[i] * pow(x, i);
   return total;
  }
 };
Hmmm, funcionalicioso! Note que utilizamos um template para definir o tipo dos coeficientes, dessa forma podemos usar inteiros, reais, matrizes; o limite é só sua ambição e o teorema da incompletude. A função principal fica da seguinte forma:
int main()
    {
  vector<double> a(3);
  a[0] = -1.0;
  a[1] = 0.0;
  a[2] = 1.0;

  Function* p = new Polynomial<double>(a);

        double result = root(p, -10.0, 10.0);
        
  cout << result;

        return 0;
    }
Criamos o vetor de coeficientes e o passamos como parâmtro para o objeto p, que é passado como parâmetro para nossa função root. O resultado é o mesmo do programa anterior (já que criamos o mesmo polinômio). Testa agora mesmo!

* Nota que dessa vez, na função de avaliação do valor do polinômio, eu utilizei a palavra-chave const. Em um post futuro vou falar mais sobre constantes em c++, mas o const depois dos parâmetros do método, significa que ele não altera nenhuma variável membro do objeto, ou seja, ele é uma classe pura. Sempre que puderes utiliza essa palavra chave, ela serve tanto para documentação (os usuários da tua classe não vão precisar olhar no corpo do método para saber se tua classe altera o estado do objeto), quanto para deixar o compilador fazer certas otimizações.

quarta-feira, 14 de março de 2012

Funtores em C++, parte I

Ponteiros para funções são legais. Mas são muito C. Para deixar nosso código mais C++, podemos usar Funtores. Funtores são classes que possuem o operator (); dessa maneira podemos chamar a função usando o identificador do objeto como uma função. Vamos transformar a função do artigo anterior em um funtor.
class Function{
 public:
  double operator ()(double x){
   return x * x - 1.0;
  }
 };
Para utilizar o funtor temos que criar uma instância dessa classe, e chamá-la através da referência ao objeto.
 Function f;
 cout << f(2);   
Para usar nossa função root, então, precisaríamos passar uma referência ao objeto:
 double root(Function &f, double i, double e);
O problema aqui é que perdemos a generalidade da função root, já que ela só funciona para um objeto do tipo Function. Para torná-la mais genérica, vamos utilizar polimorfismo. Abaixo está o código completo da solução. Na linha 6 definimos uma classe abstrata para funções, depois criamos a classe MyFunction que vai herdar de Function, sobreescremos então o operator(). Na linha 18, temos a nova assinatura da função, agora ela recebe um ponteiro para um objeto do tipo Function. Assim, com o operador new podemos criar qualquer tipo de classe que derive de Function como parâmetro de nossa função root.
 template <class T>
 bool eqlZero(T x) {
   return x * x < 0.000000001;
 }

 class Function{
 public:
  virtual double operator ()(double x) = 0;
 };
 
 class MyFunction : public Function{
 public:
  double operator ()(double x){
   return x * x - 1.0;
  }
 };
 // finds one root between i and e
 double root(Function* f, double i, double e) {
   int iterationsLeft = 10000;
   while(iterationsLeft--) {
     double x = (i+e) / 2.0;
     cout << x << endl;
     if(eqlZero((*f)(x))){
       return x;
     }else if((*f)(x) < 0) {
       i = x;
     }else {
       e = x;
     }
   }
 }

 int _tmain(int argc, _TCHAR* argv[]) {
   Function* f = new MyFunction();
   double result = root(f, -10.0, 10.0);
   cout << result;

   return 0;
 }
Teremos assim o mesmo resultado que a função do artigo anterior. Mas além de estilística, não existe uma razão aqui para usar funtores (muito pelo contrário, vamos ter uma penalidade de desempenho devido ao overhead). Na próxima parte vamos ver a vantagem de usar funtores.

domingo, 11 de março de 2012

Ponteiros para funções em c++

Ponteiros são legais, mais legal ainda são ponteiros para funções. Imagina que queremos criar uma função que encontra uma raiz de uma função em um intervalo, teremos, então, uma função que tem uma função como parâmetro.

Sua assinatura vai ser assim:
  double root(double (*f)(double), double i, double e);
O "f" é o identificador do ponteiro, do lado esquerdo fica o valor de retorno da função, e entre parênteses, do lado direito, os parâmetros. Imagina que quiséssemos um ponteiro para uma função com dois parâmetros, teríamos então:
double (*f)(double, double);
Então se quiséssemos a usar no corpo da função usaríamos, por exemplo:
 f(1.0, 5.2);
Aqui está o meu código, que acha a raíz da função x² - 1. Se quisermos usar a função root para outra função, só precisamos trocar o ponteiro.
    template <class T>
    bool eqlZero(T x) {
        return x * x < 0.000000001;
    }

    double f(double x){
        return x * x - 1.0;
    }

    // finds one root between i and e
    double root(double (*f)(double), double i, double e) {
        int iterationsLeft = 10000;
        while(iterationsLeft--) {
            double x = (i+e) / 2.0;
            cout << x << endl;
            if(eqlZero(f(x))){
                return x;
            } else if(f(x) < 0) {
                i = x;
            } else {
                e = x;
            }
        }
    }

    int _tmain(int argc, _TCHAR* argv[])
    {
        double result = root(f, -10.0, 10.0);
        cout << result;

        return 0;
    }
Esse código dá como saída o seguinte (que é quando vale a variável x a cada iteração):
0
5
2.5
1.25
0.625
0.9375
1.09375
1.01563
0.976563
0.996094
1.00586
1.00098
0.998535
0.999756
1.00037
1.00006
0.999908
0.999985
0.999985
Outra forma de implementar essa função root genérica, seria com funtores (classes com o operator ()), mas isso seria demais para esse caso. Saiba mais sobre ponteiros para funções.