A apresentação está carregando. Por favor, espere

A apresentação está carregando. Por favor, espere

Templates Traduzido de: Prepared for Bart Niswongers CSE 326 (Data Structures) class, Su 01 – Albert.

Apresentações semelhantes


Apresentação em tema: "Templates Traduzido de: Prepared for Bart Niswongers CSE 326 (Data Structures) class, Su 01 – Albert."— Transcrição da apresentação:

1 Templates Traduzido de: Prepared for Bart Niswongers CSE 326 (Data Structures) class, Su 01 – Albert Wong )

2 2 Razões de uso de templates O que são templates? Vale a pena usa-los? Programação Genérica Limitada (polimorfismo) Algumas funções tem o mesmo significado semântico para alguns (ou talvez todos) tipos de dados. A função print() deve exibir uma representação de qualquer coisa passada a ela. Não deveria haver necessidade de reescrever esta função para cada possível tipo de dado. Código menos repetitivo Código que difere apenas no tipo de dado que manipula não deve ser reescrito para cada tipo de dados que se pretende manipular. É mais fácil de ler e manter se um mesmo trecho de código for usado para todos os tipos de dados.

3 3 Exemplo: uma função swap Método estúpido – escrever uma função sobrecarregada para cada tipo void swap(int &a, int &b) { int c = a; a = b; b = c; } void swap(T &a, T &b) { T c = a; a = b; b = c; } Swap 4Swap 4 T template void swap(T &a, T &b) { T c = a; a = b; b = c; } Esta função pode ser usada com qualquer tipo de dado que suporte atribuição e pode ser passada por referência não constante. Problema: Pode ser interessante trocar os valores de duas variáveis. O comportamento desta função é semelhante para qualquer tipo de dado. Funções template permitem esta troca, na maioria das vezes sem qualquer mudança na sintaxe. Método template – escrever uma função template

4 4 Sintaxe de Template: swap dissecado template void swap(T &a, T &b) { T c = a; a = b; b = c; } A linha estabelece que tudo da declaração ou definição que se segue pertence ao template (no caso a definição da função swap) Lista de variáveis placeholder. Na maioria das vezes elas serão especificadas pelas palavras chave typename ou class. Estas palavras chave são eqüivalentes As variáveis placeholder tem um valor para cada declaração de template. Elas serão substituídas por qualquer tipo que seja especificado para o template Comportamento de Template: Como muitas coisas em C++ os templates comportam-se como parte da linguagem. Mas não são. Existem duas maneira de empregar templates, por especialização implícita ou explícita. A especialização explícita funciona sempre. A implícita algumas vezes mas é muito mais limpa

5 5 Sintaxe de Template : Uso template void swap(T &a, T &b) { T c = a; a = b; b = c; } Para usar um template há necessidade de sua especialização. Templates não são funções genéricas. Eles fazem polimorfismo estático. Eles se metamorfoseiam no tipo correto em tempo de pré- processamento Na especialização explícita de um template escreve-se seu nome com os argumentos para as variáveis placeholder entre parênteses angulosos. Isto sempre funciona. Exemplo: double d1 = 4.5, d2 = 6.7; swap (d1, d2); Templates podem detectar os valores de seus placeholders se toda a informação sobre o que o placeholder representa possa ser inferida do contexto(argumentos e, para funções membro, a instância associada). Isto é chamado de especialização implícita. No caso acima o compilador detecta que T é um double mesmo sem o explícito uma vez que os argumentos são double. Esta simplificação funciona. Exemplo: swap(d1, d2); Usando um template Sintaxe

6 6 Como funciona Pré-processador Resolve #define, #include, comments, templates Compilador Traduz o código para linguagem de máquina. Produz um object file. Este código não é executável Linker Recebe os object files e resolve as referências para as funções e variáveis em arquivos distintos Compiladores possuem no mínimo 2 partes e C++ tem 3 partes. Quando se constrói um executável a partir de um fonte C++, o pré-processador remove tudo listado sob Preprocessor. O resultado é código puro C++ (sem commentários, templates, #includes, etc). Este código é então compilado e ligado. Pré-processador.c.h.c.h Compiler Linker executável Libraries Código fonte (text) Código C/C++ (text) Código objeto (bin) Executável Nativo (bin)

7 7 Como funciona: Compilador, Linker Compilar significa transformar uma linguagem em outra linguagem. Em C++ transforma-se fonte C++, em código de máquina Cada arquivo fonte C++ usualmente é compilado em um object file que contém o código e todas as funções definidas. Caso seja invocada uma função de uma biblioteca ou outro arquivo, o código objeto (no object file) afirma esta função existe em algum lugar e quero usá-la Os erros de sintaxe usualmente são mensagens do compilador (não do linker nem do pré-processador). Compilação Depois da compilação, todos os arquivos objeto precisam ser ligados para criar um executável final. Todos os stubs quero esta função nos arquivos objeto tem de ser resolvidos em um bloco de código de máquina e o executável resultante tem de ser formatado de maneira inteligível pelo sistema operacional. Se for escrito o protótipo de uma função, sem sua definição, o código compilará mas não ligará. Os erros de link usualmente são mais difíceis de rastrear, pois linker não pode sempre fornecer os números das linhas (o linker vê apenas os arquivos objeto e não tem acesso ao código fonte. Edição de ligações

8 8 Como funciona: Pré-processador O que é o pré-processador? O pré-processador trata com as diretivas que iniciam com # (tais como #include, #define). O pré-processamento trata de tudo que ocorre antes do compilador começar a transformação para código de máquina. Em relação aos templates as coisas relevantes que o pré-processador faz são: Substitui todos os #include pelos arquivos que eles referenciam. Remove todos os comentários. Substitui todas as macros #defines por seus valores Gera o código dos templates Templates não existem! Templates são construções do pré-processador. Quando um template é usado (especializado, implicitamente or explicitamente), ele se torna instanciado. Para criar a instância o pré-processador cria versão do template na qual cada placeholder é substituído por sua especialização. Neste ponto a versão específica do template passa a existir e pode ser compilada. Ela não existe de outra maneira! Um template apenas faz uma busca e substituição para cada tipo especializado para o template.

9 9 Como funciona: Conseqüências Problema: Templates são resolvidos na etapa de pré-processamento, não existindo para o compilador até serem instanciados. Procura-se ser transparente e eficiente. Como esperado, C++ falha e às vezes as coisas se tornam inconsistentes. Efeitos: O código do template não é compilado até ser usado (e instanciado). O compilador não detecta erros de sintaxe até o template ser usado. A especialização (momento de uso do template) instancia todos os templates relevantes que o precedem. Se um template aparece depois da especialização, ele não é instanciado nem compilado. Conclusão: Para que tudo funcione, todas as definições dos templates relevantes devem aparecer antes de, pelo menos, uma especialização. Caso contrário, partes do template não serão instanciadas e compiladas.

10 10 Class Templates: Definição de Classe Sintaxe: A sintaxe para as templated classes acompanha a sintaxe das templated functions. As regras para que as templated classes possam inferir sua especialização são mais evoluídas. Recordação: Estes dois templates são equivalentes? template void swap(T &a, T &b) { T c = a; a = b; b = c; } template void swap(C &a, C &b) { C c = a; a = b; b = c; } Resposta: Sim, são equivalentes. Escrevendo class templates pode-se chegar a uma situação na qual duas definições sejam escritas para a mesma coisa. Se isto ocorrer o programa não será montado (build). O nome do placeholder não interessa, e typename e class podem ser usados de maneira intercambiável.

11 11 Class Templates: Exemplo Exemplo: Um array templated, dinâmico, 2 dimensional (Matrix)* #ifndef MATRIX_H #define MATRIX_H template class Matrix { public: Matrix(int rows, int cols); Matrix(const Matrix &other); virtual ~Matrix(); Matrix& operator=(const Matrix &rhs); T* operator[](int i); int getRows() const; int getCols() const; protected: void copy(const Matrix &other); private: Matrix(); int m_rows; int m_cols; T *m_linArray; }; #endif /* MATRIX_H */ Arquivo: Matrix.h A única coisa que muda na definição da classe é a linha: template No bloco de definição o placeholder (T) pode ser usado como um tipo de dados. Quando o template é especializado, ele assume o valor da especialização.

12 12 template T* Matrix ::operator[](int i) { return m_linArray + (i*m_cols); } template void Matrix ::copy(const Matrix &other) { m_rows = other.m_rows; m_cols = other.m_cols; int size = m_rows * m_cols; m_linArray = new T[size]; for( int i=0; i < size; i++ ) { m_linArray[i] = other.m_linArray[i]; } template int Matrix ::getRows() const { return m_rows; } template int Matrix ::getCols() const { return m_cols; } Class Templates: Continuação do exemplo #include "Matrix.h" template Matrix ::Matrix() {} template Matrix ::Matrix(int rows, int cols) { m_rows = rows; m_cols = cols; m_linArray = new T[m_rows * m_cols]; } template Matrix ::Matrix(const Matrix &other) { copy(other); } template Matrix ::~Matrix() { delete[] m_linArray; } template Matrix & Matrix ::operator=(const Matrix &other) { if( this != &other ) { delete[] m_linArray; copy(other); } return *this; } Arquivo: Matrix.cpp

13 13 template Matrix & Matrix ::operator=(const Matrix &other) { if( this != &other ) { this->~Matrix(); copy(other); } return *this; } Class Templates: Funções Membro Dissecadas O nome de uma templated class, por si só, não significa nada (Matrix não tem nenhum significado). O significado só é adquirido pela especialização (explícita ou implícita). Quando for referenciada uma instância (uma especialização específica) de uma templated class, o nome da classe deve ser especializado explícitamente. O template foi especializado implicitamente pelo seu contexto. Está dentro da região de especialização do escopo da classe. Assim não precisa dos argumentos do template. Para uma definição de classe a região de especialização é o bloco da classe. specialization region of Matrix :: Observe que a região de especialização não inclui o tipo de retorno. Portanto, o tipo de retorno necessita de especialização explícita. Lembrar que embora construtores e destrutores tenham o mesmo nome do template de classe eles são funções e não necessitam especialização.

14 14 Class Templates: As templated classes precisam ser especializadas explicitamente. Para criar uma 2 dimensional Matrix de doubles do último exemplo, a sintaxe seria: Matrix m(3,3); Sintaxe Esta especialização durante a declaração cria um novo tipo – Matrix. É um tipo próprio, separado de qualquer outra especialização de Matrix (diferente de Matrix, ou Matrix, etc.) Neste ponto a instância se comporta como qualquer outro tipo instanciado – pelo menos para efeito de compilação.

15 15 Segurança: Consciência do perigo Templates não existem até serem usados. Necessitam ser instanciados, Se a instanciação não ocorrer de maneira explicita, ela ocorre no primeiro uso de todas as definições do template. Considere-se o exemplo Problema /* main.cpp */ #include using namespace std; #include Matrix.h int main(void) { Matrix m1(3,4); cout << m1.getRows() << endl; } Parece bem mas não ligará. O que não funciona e por que? O link error ocorre com m1.getRows() Nada de um template é instanciado até que seja usado ou instanciado explicitamente. Matrix ::getRows() const não é criada até que seja usada na linha contendo m1.getRows(). A definição da função está em Matrix.cpp e nunca foi usada lá. Portanto a definição nunca é criada e compilada para código objeto. O arquivo Matrix.cpp só contém código de template. Como nunca é usado, nunca gera código objeto e não pode ser compilado.

16 16 Segurança: 3 convenções Existem três convenções para evitar o problema de ligações Escreva todo o código inline nos arquivos.h. Faça o dito anteriormente mas escreva um arquivo de implementação com a sua implementação e #include o arquivo de implementação em seu arquivo header. Escreva o template como uma classe normal (usando arquivos header e de implementação). A seguir crie um novo Este é o arquivo a ser compilado e não a implementação do template arquivo fonte e #include o arquivo de implementação do template nele. Os dois primeiros métodos sofrem do problema de, sempre que a implementação de uma função seja mudada, todo o código que a usa deve ser recompilado (e não apenas re-ligado). Isto é lento em montagens grandes. O processo de construção irá instanciar o template muito maior número de vezes do que o necessário (com perda de tempo e espaço). O terceiro método fica livre destes problemas também evitando outras barreiras uma vez que força a instanciação de tudo em um só ponto.

17 17 Segurança : Um exemplo Procedimento adequado Escreva o template, de maneira separada entre um header e um arquivo de implementação Crie um arquivo de instanciação para o template que inclua o arquivo de implementação. Compile o arquivo de instanciação e não o arquivo de implementação do template O arquivo de instanciação gera o código objeto para o template. /* main.cpp */ #include using namespace std; #include int main(void) { Matrix m1(3,4); cout << m1.getRows() << endl; } Exemplo: Para conseguir ligar de maneira apropriada o código anterior é necessário instanciar uma versão int do template de Matrix. O arquivo fica com o aspecto indicado /* MatrixInst.cc */ #include Matrix.cpp template Matrix ; Esta linha força a instanciação do template da classe Matrix, bem como de todas as suas funções membro, para a especialização int. Outras especializações exigem suas próprias linhas. Observe que o arquivo de implementação (não o header) é incluído.

18 18 Detecção de falhas (fique ligado para descobrir antes que seja tarde demais) Este código compilará? /*Foo.h */ #ifndef FOO_H #define FOO_H template class Foo { Foo() { b = Hello Mom!; } }; #endif /* FOO_H */ Infelizmente sim. Apesar de b não ser declarado, vai compilar porque ninguém instancia o template, e o compilador nunca vê o código do template. Esta é razão pela qual não forçar explicitamente a instanciação dos class templates é perigoso. Não se percebe o erro até tentar usar o código. Por esta razão algumas pessoas julgam melhor escrever uma primeira versão da classe sem usar templates, e criar templates a partir dai.

19 19 STL Standard Template Library, ou STL, é uma biclioteca C++ de classes de containers, algoritmos e iteradores Fornece muitos algoritmos básicos e estruturas de dados STL é uma biblioteca genérica com componentes are altamente parametrizados: quase todos os componentes da STL são templates. Não se deve usar a STL enquanto não se conheça o funcionamento dos templates em C++

20 20 Exemplo de uso de STL /* ********************************** * Stack.hpp * ********************************/ #ifndef STACK_HPP #define STACK_HPP #include #include template class Stack { protected: std::deque c; //container for the elements public: /* exception class for pop() and top() with empty stack /* class ReadEmptyStack : public std::exception { public: virtual const char* what() const throw() { return "read empty stack"; } }; // number of elements typename std::deque ::size_type size() const { return c.size(); } // is stack empty? bool empty() const { return c.empty(); } // push element into the stack void push (const T& elem) { c.push_back(elem); } // pop element out of the stack and // return its value T pop () { if (c.empty()) { throw ReadEmptyStack(); } T elem(c.back()); c.pop_back(); return elem; } // return value of next element T& top () { if (c.empty()) { throw ReadEmptyStack(); } return c.back(); } }; #endif /* STACK_HPP


Carregar ppt "Templates Traduzido de: Prepared for Bart Niswongers CSE 326 (Data Structures) class, Su 01 – Albert."

Apresentações semelhantes


Anúncios Google