Programação Orientada a Objetos Classes e Objetos Renato Dourado Maia Universidade Estadual de Montes Claros Engenharia de Sistemas
Onde Estamos? Da Unidade III Classes e Objetos, já foram trabalhados os seguintes tópicos: Implementando classes e objetos em C++. Atributos e métodos: controle de acesso e encapsulamento. Inicialização e destruição. Sobrecarga de funções e argumentos default. Constantes. Começaremos a ver na aula de hoje: Funções inline e controle de visibilidade. 2/31
Funções inline Uma das maneiras que a linguagem C possui para permitir uma maior eficiência do código é a u- tilização de macros. Uma macro permite que algo que seria feito por meio de uma chamada a uma função, com todo o custo associado (criando os argumentos e copiando os seus valores, fazendo um CALL em assembly, retornando o valor e efetuando um RE- TURN em assembly), seja feito pela simples substituição de código pelo pré-processador. 3/31
Macros Exemplo de macro: #define F(x) (x + 1) Utilização da macro: F(1) Código expandido pelo pré-processador: (1+1) 4/31
Macros Problema 1: #define F (x) (x + 1) F(1) (x) (x + 1)(1) Problema 2: #define CUBO(x) x*x*x. CUBO(a+b) a+b*a+b*a+b = a + 2*b*a + b Solução Problema 2 parênteses: #define CUBO(x) (x)*(x)*(x). CUBO(a+b) (a+b)*(a+b)*(a+b) 5/31
Macros Problema 3: não se pode implementar uma função membro de uma classe como uma macro! class X { int i; public: #define VAL(X::i) // Error Problemas adicionais: macros não permitem variáveis locais, blocos, verificação de tipos, chamadas recursivas, etc. Solução para todos os problemas das macros: funções inline! 6/31
Funções inline O especificador inline é uma dica para o compilador de que ele deve tentar expandir o código para a função inline, tal como a macro fazia, evitando o overhead associado à chamada à função. Todo o processo estará sobre controle do compilador. Não se pode garantir que toda chamada a uma função inline vai ser realmente gerada inline... 7/31
Funções inline Para tornar a geração de código inline possível na ausência de compiladores e links "com esperteza fora do padrão", a definição e não somente a declaração, de uma função inline deve estar no escopo da chamada à função. inline int par (int num) {return((num%2==0)? num : num+1);} inline float cubo (float x) {return x*x*x;} inline float dobro (float x) {return 2*x;} 8/31
Funções inline Toda função definida no corpo de uma classe será inline: class Circulo{ private: int raio; Ponto centro; public: Circulo(Ponto ce, int ra): centro(ce), raio(ra){ } float area( ) const { return PI*raio*raio; } }; Circulo::Circulo() e Circulo::area() são inline! 9/31
Funções inline A utilização de funções inline se justifica para funções pequenas, tais que o overhead da chamada seja maior do que o causado pela duplicação de código. Um exemplo são as chamadas funções de acesso a atributos: class Access { int i; public: int get() const { return i; } void set(int ii) { i = ii; } }; 10/31
Funções inline É comum a utilização de funções sobrecarregadas para acessar/modificar os atributos: class Rectangle { int wide, high; public: Rectangle(int w = 0, int h = 0) : wide(w), high(h) {} int width() const { return wide; } // Read void width(int w) { wide = w; } // Set int height() const { return high; } // Read void height(int h) { high = h; } // Set }; int main() { Rectangle r(19, 47); r.height(2 * r.width()); r.width(2 * r.height()); } ///:~ 11/31
Funções inline A utilização de funções inline na definição da classe faz com que a sua interface fique poluída com detalhes de implementação que deveriam estar sendo escondidos do usuário da classe... Para evitar isso e ainda poder utilizar funções inline, funções membro inline podem ser definidas fora da definição da classe, mas ainda no arquivo.h. 12/31
Funções inline class Rectangle { int width, height; public: Rectangle(int w = 0, int h = 0); int getwidth() const; void setwidth(int w); int getheight() const; void setheight(int h); }; inline Rectangle::Rectangle(int w, int h) : width(w), height(h) {} inline int Rectangle::getWidth() const { return width;} inline void Rectangle::setWidth(int w) { width = w;} inline int Rectangle::getHeight() const { return height;} inline void Rectangle::setHeight(int h) { height = h;} 13/31
Variáveis static int count ( ) { static int num = 0; // inicializada uma única vez int x = 0; // inicializada várias vezes num++; return num; } A inicialização da variável local num é feita somente na primeira vez em que a função é chamada (na primeira vez em que o programa passa pela declaração da variável) e, portanto, o seu valor é mantido de uma chamada para outra: isso significa que a variável static não é criada na pilha, mas sim na área de variáveis estáticas do programa. 14/31
Objetos static O construtor de um objeto static é chamado apenas uma vez, no momento em que a thread de e- xecução executa pela primeira vez o código da função. O destrutor de um objeto static somente é executado ao final do programa. O construtor de um objeto global é executado antes do main() começar a sua execução. O destrutor de um objeto global é executado somente ao final da execução do programa. 15/31
#include <iostream> using namespace std; Objetos static class Obj { char c; // Identifier public: Obj(char cc) : c(cc) { cout << "Obj::Obj() for " << c << endl; } ~Obj() { cout << "Obj::~Obj() for " << c << endl; } }; Obj a('a'); // Global void f() { static Obj b('b');} void g() { static Obj c('c');} int main() { cout << "inside main()" << endl; f(); // Calls static constructor for b // g() not called, c not constructed cout << "leaving main()" << endl; } ///:~ 16/31
Atributos static Atributos estáticos servem para implementar o conceito de Atributo de Classe: Deseja-se que todos os objetos de uma determinada classe compartilhem um certo dado. class Ponto{ int x, y; static int cont; //... }; Ponto Ponto_1, Ponto_2; 17/31
Atributos static Ponto_1 x y cont Ponto_2 x y cont 18/31
Atributos static O atributo estático pode ser acessado por funções membro ou por outras partes do programa, quando público. Ele pode, inclusive, ser acessado antes da existência de qualquer objeto da classe, já que o espaço de memória que ele ocupa é reservado antes da criação de qualquer objeto. 19/31
Atributos static Todo membro estático deve ser redeclarado fora da classe, mas dentro do escopo do arquivo da classe, ponto em que ele pode ser inicializado. Não se pode inicializar um membro estático não constante dentro da definição da classe! class Ponto{ int x, y; static int cont; //... }; int Ponto :: cont = 7; Se o atributo static for constante, pode ser inicializado na própria classe class Buffer{ static const int buffsize = 256; char buff[buffsize]; //... }; 20/31
Funções Membro static Funções membro estáticas são um tipo de função membro especial cujas principais características são: Podem ser chamadas sem estar associadas a um objeto da classe. Não podem manipular membros não estáticos da classe, ou seja, são criadas para manipular os atributos estáticos de uma classe! 21/31
#include <iostream.h> class Ponto{ int x, y; static int cont; public: Ponto( ){ cont ++;} ~Ponto(); static void mostra () { cout << cont << " "; } }; int Ponto::cont = 0; Ponto :: ~Ponto () { Funções Membro static int main() { Ponto :: mostra(); // Sem objeto declarado Ponto p1; p1.mostra(); { Ponto p2; p2.mostra(); } Ponto p2; p2.mostra(); return 0; } cout << "Destruindo Ponto " << cont << endl; cont --; } 22/31
Padrão de Projeto Singleton Objetivo: criar uma classe que possa possuir apenas uma instância que esteja disponível globalmente no código. Motivação: há várias situações em que é útil criar uma classe que pode possuir apenas uma instância. Exemplos: deve existir apenas um sistema de arquivos, apesar de poderem existir várias impressoras, deve e- xistir apenas um spool de impressão, apenas um conversor A/D para um filtro digital, etc. 23/31
Padrão de Projeto Singleton A instância global deve ser facilmente localizável, mas criar um ponteiro global não é recomendável: Poluição do namespace global. Objetos globais são criados antes de se entrar no main, o que é um problema nos casos em que alguma informação adicional seja necessária para a criação do Singleton... 24/31
Padrão de Projeto Singleton O construtor da classe é privado, de modo que a criação da instância única é feita no método Instance(). Instance() é um método de classe static e, portanto, pode ser chamado antes mesmo da existência do objeto. Uma vez que a instância exista, Instance() simplesmente retorna um ponteiro para ela. 25/31
Padrão de Projeto Singleton Singleton static InstancePtr static Instance( ) Singleton( ) return InstancePtr 26/31
Padrão de Projeto Singleton Exemplo: considere uma classe que encapsula um buffer de teclado, armazenando todas as teclas pressionadas pelo usuário. Pode-se garantir que existirá uma única instância do buffer, tornando a classe um Singleton. KbdBuffer static InstancePtr : KbdBuffer* static Instance () : KbdBuffer* KbdBuffer () getch () : char return InstancePtr 27/31
Exemplo Singleton class KbdBuffer { public: char getch(); // reads from the buffer static KbdBuffer *Instance(); // returns pointer to instance private: KbdBuffer(); // the constructor static KbdBuffer *instanceptr; // pointer to instance }; KbdBuffer *KBdBuffer::instancePtr = 0; // reset pointer KbdBuffer *KbdBuffer::Instance () { if (instanceptr == 0) // if pointer is null... instanceptr = new KbdBuffer; //... create instance return(instanceptr) } Utilização: char foo = KbdBuffer::Instance() -> getch(); 28/31
Singleton Consequências Como existe apenas uma instância da classe, o programador tem controle sobre como os clientes a acessam! Reduz a poluição do namespace global, pois apenas o nome da classe entra nele! Exemplos interessantes são as classes Singleton criadas por Wizards na programação com a MFC no Visual C++. 29/31
Por enquanto, isso é tudo, pessoal! 30/31
Importante Esta apresentação é uma adaptação do material originalmente desenvolvido pelo professor Renato Cardoso Mesquita, do Departamento de Engenharia Elétrica da Universidade Federal de Minas Gerais. http://www.cpdee.ufmg.br/~renato/ 31/31