Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Objetos em C++
A programação orientada a objetos no
contexto geral do desenvolvimento de
Ivan Luiz Marques Ricarte
software
Do projeto de software para a
FEEC/UNICAMP
implementação em C++
Técnicas de implementação inerentes à
linguagem de programação C++
Sistemas computacionais onipresentes Software como um produto
software é componente essencial nesses primeiras bibliotecas de software
sistemas Alto custo de manutenção
Software é um produto em muitos casos, manutenção era
resultado do desenvolvimento entregue junto virtualmente impossível
ao sistema computacional
Software é um veículo para disponibilizar
o produto
1950 1960 1970 1980 1990 2000
Os primórdios do desenvolvimento de
software A terceira era
software como um afterthought Amplo uso de microprocessadores
custom software sistemas embarcados
custo de hardware baixo
quase nenhum método sistemático de
desenvolvimento
1950 1960 1970 1980 1990 2000 1950 1960 1970 1980 1990 2000
A era atual no desenvolvimento de
software Mitos de software
Sistemas decentralizados e Precisa desenvolver software?
incorporando novas tecnologias Vamos comprar um computador da última
geração
Inteligência artificial, computação paralela,
realidade virtual, multimídia O que é preciso para começar a
software mais caro que hardware programar?
OO como tecnologia de integração Basta uma descrição genérica de objetivos
O desenvolvimento está atrasado?
Vamos acrescentar alguns programadores a
1950 1960 1970 1980 1990 2000
mais para agilizar e entrar em fase
Problemas recorrentes no
desenvolvimento de software E mais mitos
Maior parte do software ainda é Houve algumas mudanças nos requisitos do
projeto; algum problema?
Não, software é flexível
como explorar potencial do hardware?
como atender às demandas por novas Como saber se o projeto obteve sucesso?
The establishment and use of sound engineering Processos
principles in order to obtain economically software that is
reliable and works efficiently on real machines. estabelecem a base para o controle
administrativo de projetos de software
Fritz Bauer, 1969
(1) The application of a systematic, disciplined, Métodos
quantifiable approach to the development, operation, and aspectos técnicos (os how to s ) da
maintenance of software; that is, the application of construção de programas
engineering to software.
Ferramentas
(2) The study of approaches as in (1).
IEEE, 1993 apoio automatizado a processos e métodos
O que é a
engenharia de software? Modelo de processo linear
tools system/information
engineering and modeling
methods
process analysis design code test
a quality focus
maintenance
Etapas genéricas do processo: Etapas genéricas no processo:
Análise Codificação
Análise de requisitos Tradução do projeto de software em
ponte entre engenharia de sistemas e o programas
projeto de software uma ou mais linguagens de programação
reconhecimento do problema, avaliação, Incorporação de opções de
síntese, modelagem, especificação e revisão
implementação
Análise de software
fidelidade ao projeto, eficiência, reutilização
primeira representação técnica do sistema de código, adaptação a recursos disponíveis
modelagem dos dados, seus atributos e
relacionamentos, e das funções que os
manipulam
dados, procedimentos, interfaces e aos requisitos estabelecidos
arquitetura Estratégias
business modeling
application
generation
em decorrência do resultado de testes ou de data process
testing &
modeling turnover
novas demandas colocadas para o sistema modeling
application
process generation
Requer um ciclo completo de testing &
modeling turnover
desenvolvimento
application
análise, projeto, codificação e teste generation
testing &
turnover
60−90 days
gathering Listen to
customer Prototype Diagramas de fluxo de dados
construction
Build/revise Atividades no projeto
mock−up
Estabelecimento de arquitetura do sistema
customer
Linguagem de programação estruturada
Customer
test−drives Prototype
mock−up evaluation
Características de um bom projeto de A construção de software orientado a
software estruturado objetos
Abstração Uso de linguagem de programação que
de dados, de procedimentos e de controle suporte as mesmas primitivas da análise
refinamento top−down e projeto
classes, objetos, herança, relações, ...
Arquitetura modular
solução como integração de módulos com O modelo de orientação a objetos permite
alta coesão e baixo acoplamento levar naturalmente às boas características
de software
Ocultamento da informação
mas maus hábitos de programação podem
reduz impacto de alterações e propagação de levar tudo a perder!
erros
A metodologia de desenvolvimento de
software orientado a objetos Programação Orientada a
Objetos
A mesma linguagem na análise e
projeto
Análise: modelos conceituais
casos de uso
classes, atributos no domínio do problema
Do projeto para o
diagramas de seqüência e contratos
programa
Projeto: opções de desenvolvimento
uso de componentes e interfaces
visibilidade e organização em pacotes
Resultados da atividade de projeto Evolução dos paradigmas de
programação: orientada a objetos
Especificação detalhada do sistema Foco na definição de classes
Diagramas, descrições funcionais e estruturais definição estrutural de classes
Projeto estruturado classes básicas, atributos, declaração de métodos
classes derivadas
procedimentos, módulos, interface de
programação para módulos, estruturas de definição do comportamento de classes
dados internas definição de métodos
Projeto orientado a objetos Programas de teste e programa principal
classes, métodos, atributos, colaborações após definição completa de classes
Abordagem top−down Técnica para escrever bons programas para
primeira descrição: programa principal um determinado conjunto de problemas
procedimentos usados no programa principal são Linguagem de programação orientada a
refinados sucessivamente até que procedimentos
primitivos sejam alcançados objetos
Arquitetura modular Linguagem de programação que oferece
mecanismos adequados ao estilo de
estrutura interna de organização
programação orientada a objetos
mecanismo para abstração
Características de linguagens de Linguagens recentes de
programação orientada a objetos programação orientada a objetos
Definição de classes C++
criação de tipos definidos pelo programador extensão da linguagem C para incorporar
conjunto de atributos e métodos associados mecanismos da programação orientada a
objetos
Herança meados da década de 1980
definição de classes a partir da Java
especialização de classes existentes
uma nova linguagem orientada a objetos
Criação e remoção de objetos usando uma sintaxe no estilo da linguagem C
manipulação da região de memória definida meados da década de 1990
pelo objeto
Primeiras linguagens de
programação orientada a objetos Programação orientada a objetos
Simula 67
aplicações em simulação
introduziu conceito de classes e objetos
Smalltalk 80 Introdução à
linguagem “completamente orientada a
objetos” desenvolvida na Xerox PARC linguagem C++
baixa eficiência
considerada uma “linguagem de brinquedo” por
“desenvolvedores de verdade”
Introduction to C++ C++ evolution
Programming language to improve the C C++ predecessor: C with classes (1980)
programming language supporting: Stroustrup approach to support development
data abstraction of event−driven simulation programs from C
object−oriented programming C++ first public appearance
Designed by Bjarne Stroustrup July 1983
AT&T Bell Labs since then, use of C++ has grown explosively
initial motivation C++ standardization effort
development of efficient simulation programs Starting 1987
ANSI committee, 1989−1996
C++ is mostly derived from C class class_name {
originally a systems programming language private:
Unix operating system project private−member−declarations
generated code is time and space efficient public:
derived from BCPL and B public−member−declarations
protected:
Concepts from many other languages
protected−member−declarations
Simula67: classes
};
Algol68: operator overloading
ADA and Clu: templates, exception handling
C++: Class definition C++: Class definition
Members are class attributes (variables) or Static member
class methods (functions) member of a class that is shared by all
private members can only be accessed from objects of the class
member methods keyword static
public members accessible from any function static attribute member
protected members accessible from derived same value viewed by all objects
classes static method member
applied to the class, not to objects
Attribute members are defined within the Instances of classes
method definition within class definition: inline deleted by end of scope
returntype classname::methodname(args){body}
created by operator new
Object initialization and finalization Abstract classes
constructors: contain at least one virtual function
same name of class concrete derived classes should provide
no return value (not even void), implementation for these functions
can have arguments
member functions bound dynamically, during
automatic invocation when object is created run time, to the function that will be called
destructors: when function invoked through reference to object
name of the class prefixed by ~ in C++, reference to object: pointer
no return value, no arguments
automatic invocation when object is destroyed
// only additions to derived class are specified defines a function main that takes no
... arguments and does nothing
class derived : public base1, public base2 { Every C++ program has a main function
...
starting point for program execution
}
C++ syntax C++ syntax
main function can present arguments and Fundamental types
return a value char
int main(int argc,char *argv[]){ short int or simply short Integral types
return 0; int
}
long int or simply long
argc: argument count
float Floating−point
argv: argument values double
numbers
return value: to the invoking program
long double
(operating system)
void
declaration + − * / % ++ −−
also == != < > <= >=
initialization
Boolean connectives: && || !
const modifier
storage modifiers static and extern Bit operators: & | ~ ^
sizeof operator
C++ syntax C++ syntax
Arrays Loops
char v[10]; while (logical condition) {
v has elements indexed from 0 to 9 ...
Pointers }
char *p; also
Operator address−of do while and for statements
p = &v[3]; break, goto, continue jump statements
Logical condition is an integer expression
Tests Functions
if (logical condition) { return_type function (param_list) {
... function body
} }
else { also
} function declarations
ternary operator ? :
C++ syntax C++ programming
Functions Common design pitfalls
argument passing by reference is possible Ignore classes, using C subset only
pointer manipulation is implicit Ignore derived classes and virtual functions
operator & use data abstraction subset
different signatures for same function name Ignore static type checking
function signature determines which design constraining implementers to dynamic type
implementation is selected checking only
static (compile−time) checking
It is possible to use C++ and do
exclusively procedural programming
Trabalhando com objetos
C++ can be used as a dialect of C
Development of good object−oriented
programs in C++ depends on
programming discipline
supported by application of object−oriented
practices in analysis and design
A criação de objetos Exemplos de construtores default
Criação de um objeto de uma classe dá− class Xyz { class Xyz { class Xyz {
se por meio da invocação de um de seus ... ... ...
métodos construtores } public: public:
construtor default Xyz(int a); Xyz(int a=0);
invocado sem argumentos main() { } }
construtor de cópia Xyz o1; main() { main() {
invocado com um argumento da mesma classe Xyz o1; Xyz o1;
...
construtor com argumentos ... ...
}
invocado em outros casos
} erro! }
construtor sem argumentos é disponibilizado outro objeto existente
pelo compilador se nenhum outro construtor recebe como argumento uma referência para
for definido um objeto da mesma classe
Definido pelo programador class Xyz {
sem argumentos ...
public:
com argumentos, desde que todos possam
receber algum valor por default Xyz(Xyz& a);
}
Construtor de cópia automático Construção de objetos
Construtor de cópia é automaticamente Estaticamente
definido pelo compilador declaração de objeto
quando programador não define escopo válido até o fim do bloco onde ocorre
explicitamente um construtor de cópia declaração
Comportamento do construtor automático Dinamicamente
cria novo objeto da classe declaração de ponteiro para objeto
faz cópia membro a membro criado com operador new
válido até invocação do operador delete para
o ponteiro
int x; int y;
Situações onde é necessário definir public:
Xyz(int a=0, int b=0);
explicitamente um construtor de cópia Xyz(Xyz& a);
};
Xyz::Xyz(int a, int b) {
quando parte do objeto faz uso de recursos x = a; y = b;
externos aos seus membros cout << "Xyz criado: (" << x << "," << y << ")" << endl;
}
áreas alocadas de memória, arquivos Xyz::Xyz(Xyz& a) {
x = a.x; y = a.y;
cout << "Xyz copiado: (" << x << "," << y << ")" << endl;
Opções de cópia com recursos externos }
int main(int argc, char *argv[]) {
cópia rasa Xyz o1;
Xyz o2(1,1);
com compartilhamento Xyz o3 = o2;
Xyz o4 = Xyz(2,2);
Xyz o5 = 1;
cópia profunda Xyz* o6 = new Xyz(3,3);
return 0;
com alocação de novos recursos }
Resultado da execução para
construção de objetos Operando com arranjos de objetos
Xyz criado: (0,0) Para a criação de a1 e a4, construtor sem
Xyz criado: (1,1) argumentos é utilizado
deve estar presente
Xyz copiado: (1,1)
A remoção de arranjos alocados
Xyz criado: (2,2)
dinamicamente deve ser explícita
Xyz criado: (1,0) delete [] a4;
Xyz criado: (3,3) de outro modo, apenas o primeiro elemento é
liberado
seria o comportamento padrão pelo fim do escopo
Xyz a1[4]; Operador global e static
cria arranjo com quatro objetos (0,0) Obtém espaço da área livre
Xyz a2[] = {1, 2}; Pode ser redefinido na classe
cria arranjo com dois objetos, (1,0) e (2,0) void *operator new(size_t s);
Xyz a3[4] = {Xyz(1,1), Xyz(2,2)}; Pode incluir parâmetros
cria arranjo com (1,1), (2,2), (0,0) e (0,0) void *operator new(size_t s, params);
Uso: Xyz* x = new (args) Xyz(args_cons);
cria arranjo com quatro objetos (0,0) Pode ser sobrecarregado
Complementa informação para a Quando estado de objeto é descrito
construção do objeto exclusivamente por seus membros
Classe::Classe(params) : lista_inicializadores {...} locais , comportamento padrão de
Possíveis usos: remoção funciona bem
iniciar valores de membros constantes liberar a área de memória ocupada
iniciar objetos membros com construtores Quando outros recursos externos estão
diferentes do default
alocados ao objeto, uso de destrutores é
invocar construtores diferentes do default nas necessário
superclasses
Quando um objeto de classe T é criado Método destrutor para classe T é T::~T()
1. construtores das classes bases são sem argumentos
lista de inicializadores
invocado automaticamente ao fim do escopo
invocado automaticamente pelo operador delete
É um método static da classe: Duas maneiras de copiar objetos
Abc* pa; por atribuição
... uso do operador de atribuição =
pa −> Abc::~Abc();
por inicialização
realiza um clean−up sem remover o objeto
na inicialização de objetos
neste caso, destrutor precisa ter sido incluindo declaração com valor inicial
declarado explicitamente na passagem de argumentos por valor
apenas um destrutor pode ser definido portanto não deve existir construtor T(T) para classe T
assinatura é fixa, não permite sobrecarga como retorno de funções e métodos
3. os destrutores das classes bases são membros que são ponteiros para áreas alocadas
invocados
Sobrecarga do operador =
Processo inverso ao da criação de objetos Abc& Abc::operator =(Abc& rhs)
int x; int y;
Não é possível inventar novos operadores public:
...
restrito aos existentes em C++ Xyz operator +(Xyz a);
};
Não é possível redefinir alguns ...
Xyz Xyz::operator +(Xyz a) {
operadores Xyz z;
z.x = x + a.x;
. .* :: ?: z.y = y + a.y;
return z;
# ## }
int main() {
Não é possível mudar associatividade ou Xyz o1(2,2);
Xyz o2(1,1);
precedência de operadores Xyz o3 = o1 + o2;
Xyz o4 = o2 + 1;
Não é possível mudar número de // Xyz o5 = 1 + o2; <=== Situação de erro de compilação
}
operandos de operadores
para classe T, Xyz copiado: (3,3)
Xyz o3 = o1 + o2; class X { class Y; class Y;
friend void f(); class X { class X {
o3 = o1.operator+(o2);
} friend void Y::f(); friend void Y;
todos os argumentos de acordo com assinatura
} }
Xyz o4 = o2 + 1; void f() { class Y { class Y {
o4 = o2.operator+(1); ... void f(); ...
sabe como criar Xyz a partir de um inteiro } } }
Xyz o5 = 1 + o2; métodos com direito void Y::f() { todos os métodos de
o5 = 1.operator+(o2); de acesso aos membros ... Y têm direito de
internos de X } acesso aos membros
não pode aplicar operador a um valor inteiro... internos de X
Características de funções e
Friends operadores amigos
Funções que são amigas de uma classe São globais
recebem direito de acesso aos membros podem pertencer a mais de uma classe
Quando uma classe é declarada como amiga não fazem referência a um objeto específico
Quando acesso a membros internos de Conversão pode ocorrer por meio de
mais de uma classe é desejado ou construtores e por meio de operadores
necessário class Xyz {
...
operação conjunta das classes está public:
fortemente relacionada Xyz (OutroTipo ot); // de OutroTipo para Xyz
operator OutroTipo( ); // de Xyz para OutroTipo
Exemplos }
...
Matriz e Vetor (aplicação em álgebra linear) Xyz x; OutroTipo w;
w = OutroTipo(x); // OK
Container e Iterator (coleções de objetos) w = (OutroTipo) x; // Também OK
w = x; // OK, conversão implícita
...
Função amiga não é aplicada a um objeto public:
friend Xyz operator +(Xyz a, Xyz b);
recebe objeto como argumento ...
};
Pode ser aplicada tanto ao objeto da Xyz operator +(Xyz a, Xyz b) {
Xyz z;
classe especificada no parâmetro como a z.x = a.x + b.x;
z.y = a.y + b.y;
qualquer instância de outro tipo que possa return z;
ser convertido para ela }
int main() {
argumentos de funções e operadores amigos Xyz o1(2,2);
Xyz o2(1,1);
são tratados simetricamente Xyz o3 = o1 + o2;
Xyz o4 = o2 + 1;
Xyz o5 = 1 + o2; // Agora OK
...
}
Uso de funções/operadores
amigos: extensão streams Quando não usar friend?
Forma de uso desejada dos operadores Quando não houver nenhum motivo que
<< e >> justifique o uso de função amiga, dê
Xyz x; preferência ao método
cin >> x; reduz uso do espaço global de nomes
cout << x;
Se não há conversão definida
não devem ser membros de ostream e
istream Quando operador requer um lado
não seria possível prever todas as situações esquerdo que seja do tipo de objeto
não podem ser membros de Xyz = , += , −=, ..., ++ , −−
lado esquerdo não é objeto Xyz
friend istream& operator>>(istream&, Xyz& a); praticamente impossível pensar que uma
} seria independente da outra
Objetos da nova classe constituem um
subconjunto dos objetos da classe base
Reaproveitando
associação do tipo é−um
funcionalidades definidas
em outras classes Princípio da substituição de Liskov
Relacionamento hierárquico é
estabelecido entre as classes
classe base: funcionalidades genéricas
classe derivada: funcionalidades
especializadas
Derivação de classes funcionalidades à definição da classe base
implementação do conceito de herança Especificação
implementação do conceito de associações entre funcionalidades declaradas na classe base
Especificação de classe derivada: class Base {
class Derivada : especificador_acesso Base { int a;
class Deriv1 : Base {
... protected:
public:
}; int b;
void fd();
objetos de Derivada podem manipular public:
};
diretamente membros public e protected int c;
void Deriv1::fd() {
definidos em Base };
mas não têm acesso a membros private ...
} Base::a is not accessible
in function Deriv1::fd()
private (default) int main() {
todos os membros public e protected da classe Base b1;
Base tornam−se membros private de Derivada Deriv1 d1;
Base::c is not accessible in
protected b1.c++; function main()
todos os membros public e protected da classe d1.c++;
Base tornam−se membros protected de Derivada
d1.fd();
Base* bp = &d1;
membros public e protected da classe Base Base * in function main()
mantêm visibilidade na classe Derivada bp−>c++;
}
Reajuste de permissões de acesso O que era público na base continua
a membros público na derivada
Visibilidade da classe base pode ser int main() {
individualmente restaurada na classe Deriv2 d1; Agora OK.
derivada d1.c++;
class Deriv1 : private Base { d1.fd();
protected:
Base* bp = &d1;
Base::b;
bp−>c++; Também OK.
public:
void fd(); }
};
public:
int b; Todos os métodos (funções membros)
void fd();
int c; Construtores
void Deriv2::fd() {
}; Destrutores
...
} Base::a is not accessible
in function Deriv1::fd()
Especificando a construção da
Objetos derivados e construtores parte base
Construção de objeto começa pela parte Incluir construtor default na classe base
derivada da classe base se tiver acesso à classe base
Construtor da classe derivada sempre invoca se fizer sentido
construtor da classe base
Referenciar construtor válido na lista de
Construtor default (sem argumentos) é
invocado automaticamente
inicializadores
Caso não exista construtor default na classe Deriv1::Deriv1( ) : Base(0) {
base, invocação explícita deve estar na lista ...
de inicializadores do construtor derivado }
};
complementa cópia com parte específica
Base::Base(int i) { ... }
class Deriv1 : public Base { Construtor de cópia definido pelo
... programador
} initialize base class Base in function }
Deriv1::Deriv1()
Atribuição entre objetos de classes Usando operador de atribuição
bases e derivadas automático
class Base {
De classe derivada para classe base
public:
ocorre sem problemas Base& operator=(Base& b);
descarta parte especializada };
Base& Base::operator=(Base& b) {
De classe base para derivada cout << "Atribuicao base" << endl;
não é permitida (parte indefinida) return b;
int main() { }
Deriv d1; Base b1; Ok class Deriv1 : public Base {...};
b1 = d1; int main() {
Deriv1 d1, d2;
d1 = b1; Atribuição base é
Cannot convert Base to
} d2 = d1; invocada
Deriv in function main() }
class Base { public: Base& operator=(Base& b); };
Semântica para operador definido
Base& Base::operator=(Base& b) { ... }
automaticamente: class Deriv : public Base {
invoca operador de atribuição para classe public: Deriv& operator=(Deriv& d); };
base Deriv& Deriv::operator=(Deriv& d) {
executa atribuição membro a membro para a cout << "Atribuicao da parte derivada" << endl;
parte específica da classe derivada return d;
}
Semântica para operador é redefinido: int main() {
responsabilidade do programador Deriv d1, d2; Atribuição base
d2 = d1; não é invocada
}
Usando todos operadores de
atribuição especificados Cuidados com uso de ponteiros
class Base { ... };
Porquê dos ponteiros
Base& Base::operator=(Base& b) { ... }
class Deriv : public Base { ... }; permitem programação mais flexível
Deriv& Deriv::operator=(Deriv& d) { definição do objeto manipulado apenas no
(Base&) (*this) = d; momento da execução
cout << "Atribuicao da parte derivada" << endl;
permitem aplicação do princípio da
return d;
}
substituição de Liskov
int main() { onde houver ponteiro para classe base, objeto de
Deriv d1, d2; qualquer classe derivada pode ser referenciado
Atribuição base é invocada e,
d2 = d1;
depois, atribuição derivada
}
Se destrutor base for declarado virtual Definição derivada esconde original
class Base { ... mesmo que assinatura seja diferente
class Base {public: void f(); };
virtual ~Base();
void Base::f() { ... }
} class Deriv : public Base {public: void f(int x); };
int main() { Construtor Base void Deriv::f(int x) { ... }
Construtor Deriv1 int main() {
Base* pb = new Deriv1; Deriv d1;
... d1.f(); Erro de compilação:
delete pb; Destrutor Deriv1 d1.f(1); Too few parameters in call to
Destrutor Base } Deriv1::f(int) in function main()
}
esconde o membro original ...
class Base {public: void f(); }; void Deriv::f() { Referência à
void Base::f() { cout << "Base::f()" << endl; } Base::f();
definição original
class Deriv : public Base {public: void f(); }; cout << "Deriv::f()" << endl;
void Deriv::f() { cout << "Deriv::f()" << endl; } }
int main() { int main() {
Base::f( )
Base b1; b1.f(); Deriv d1;
Deriv d1; d1.f(); d1.f(); Base::f()
} Deriv::f( ) } Deriv::f()
Acesso a membros de mesmo Conversão entre classes bases e
nome por ponteiros derivadas
No caso geral, o tipo do ponteiro é que Ponteiro para objeto de classe derivada é
predomina automaticamente convertido para ponteiro
... de classe base
int main() { apenas membros da classe base são
Base *b1, *b2; referenciáveis através do ponteiro
b1 = new Base; Base::f()
para métodos declarados como virtuais, se
b1 −> f( );
um método redefinido com mesma assinatura
b2 = new Deriv;
b2 −> f( );
estiver presente na classe derivada então
} Base::f() será usado. Senão, usa método da classe
base
public:
virtual void f(); não contém nenhuma implementação
}; class Base {
int main() { public:
Base *b1, *b2;
virtual void f( ) = 0;
b1 = new Base; Base::f()
};
b1−>f();
b2 = new Deriv;
b2−>f();
}
Deriv::f()
Herança múltipla com base
Classes abstratas repetida
Contêm funções virtuais puras class Base { ... };
class Base1 : public Base { ... };
Só podem ser utilizadas como base para
class Base2 : public Base { ... };
a definição de outras classes
class Deriv : public Base1, public Base2 {
não é possível criar objetos de classe
...
abstrata
class Abst {
}; construtor de Base
construtor de Base1
public: int main() {
construtor de Base
virtual void f() = 0; Deriv d1; construtor de Base2
destrutor de Deriv construtor de Deriv
}; ...
Cannot create instance of abstract destrutor de Base2
int main() { class Abst in function main() destrutor de Base
}
Abst a1; Class Abst is abstract because of destrutor de Base1
} Abst::f( ) = 0 destrutor de Base
Mesmos princípios da herança simples class Base { ... };
class Base1 { ... }; class Base1 : public virtual Base { ... };
class Base2 { ... }; class Base2 : public virtual Base { ... };
class Deriv : public Base1, public Base2 { class Deriv : public Base1, public Base2 {
... ...
construtor de Base1 };
};
construtor de Base2 int main() { construtor de Base
int main() { construtor de Base1
construtor de Deriv Deriv d1; construtor de Base2
Deriv d1;
... construtor de Deriv
... destrutor de Deriv destrutor de Deriv
destrutor de Base2 } destrutor de Base2
} destrutor de Base1
destrutor de Base1 destrutor de Base
Quando não usar herança Programação Orientada a
Objetos com C++
Hierarquia de classes contém contração
redução de funcionalidades nas classes
derivadas Tratamento de
Relação entre as classes pode ser melhor exceções
descrita por composição
é parte de , tem um
class Part2 { ... }; Comp não oferece os mesmos separar código para tratamento de erros do
Modelo baseado em dois eventos Uso do comando throw
síncronos throw expressão;
sinalização da ocorrência da situação de Comando executado quando a situação de
exceção
erro é detectada
throwing an exception
Encerra a execução do código corrente
resposta à situação de exceção sinalizada Similar a um comando return
catching an exception
Ocorrência do evento de sinalização
transfere controle do ponto de execução
para um tratador de exceções previamente
cadastrado
Código pode encontrar uma condição que O tipo resultante da expressão throw
não pode suportar identifica a exceção
Eventos ocorrem em partes distintas do Seu conteúdo é inicializado para o valor da
código expressão
Podem até mesmo ter sido definidos por Objeto estático: garante que mesmo erros
programadores distintos associados à manipulação da área livre
podem ser tratados
Reconhecendo a ocorrência da
exceção Múltiplos catches
try {
Instrução try−catch ...
try { }
... // código que pode gerar a exceção catch (tipo1 e1) {
... // em algum método utilizado ...
} }
catch (tipo_de_exceção e) { catch (tipo2 e2) {
... // código de tratamento para esse Catch genérico
...
... // tipo de exceção }
} catch (...) {
...
}
throw Socorro! ; // ... mais recentemente definido
}
Mesmo para objetos parcialmente construídos o
compilador deve tratar corretamente a destruição
parcial
Tratando a exceção Continuando a execução
Ativa código do catch correspondente ao Após código do tratamento, execução
tipo da exceção gerada continua após o bloco try
Correspondente: Não há retorno para ponto de ocorrência do
Catch e throw têm mesmo tipo erro
Catch tem classe que é base para tipo de throw Se execução das instruções no corpo do
Catch é ponteiro e throw gera ponteiro que pode bloco try não causa a geração de
ser convertido para o ponteiro capturado por uma
conversão padrão de ponteiros nenhuma condição de exceção, blocos
catches são ignorados
Os mais genéricos devem aparecer após Que exceções podem ocorrer no corpo da
casos especializados função
A declaração genérica (...), se presente, deve tipo func(param) throw (lista_tipos_exceções);
ser a última
Se não houver nenhuma combinação, tipo func(param) throw (lista_tipos_exceções) {
propaga exceção ... // código que pode gerar ou propagar
Equivale a ter "catch(...) { throw; }" ... // as exceções indicadas
}
Unexpected Propagação de exceções
Função unexpected() é invocada quando No tratamento de uma exceção, o código
uma função gera uma exceção não de manipulação pode optar por repassar a
especificada exceção que o ativou:
Efeito default de unexpected(): invocar ...
terminate() throw;
Efeito default de terminate(): invocar abort() ...
Reflete um erro sério de projeto encerra execução do manipulador e de seu
correspondente bloco try−catch
repassa controle ao próximo bloco try−catch
Função com lista de especificação vazia, condição de erro além da simples sinalização
de sua ocorrência
podem ser agrupadas em hierarquias de
Função com lista de especificação exceções
contendo a classe B pode gerar exceções permite tratamento genérico para um grupo de
associadas a objetos de qualquer classe exceções de mesma raiz
derivada publicamente de B
Programação Orientada a Saída
Objetos com C++
Classe ostream
Sobrecarrega operador <<
Streams inserção ou put to
Definido na classe base com lado direito
correspondente a todos os tipos básicos da
linguagem
qualquer outro tipo: deve ser definido como
apresentar valores
correspondente a todos os tipos básicos da
declara os objetos associados aos linguagem
dispositivos padrões de entrada e saída qualquer outro tipo: deve ser definido como obter valores
clog − saída padrão de erros (com buffer)
Manipuladores de streams Formatação
ostream& flush(ostream&) métodos da classe ios
efetiva qualquer pendência nos buffers de afetam próxima operação apenas
saída width(int w) e int width()
ostream& endl(ostream&) largura de campo de apresentação
escreve nova linha e flush fill(char) e char fill()
setw(int) caráter de preenchimento da largura
precision(int) e int precision()
define máxima quantidade de elementos na
leitura precisão de apresentação de valores reais
char name[MAX];
cin >> setw(MAX−1) >> name;
Métodos flags para controle de formato:
left, right
scientific, fixed
associados a manipuladores
stream corrompido
Arquivos de leitura Arquivos de entrada e saída
Declarações em fstream.h Classe fstream
arquivo seqüencial de entrada: ifstream Derivada de fstreambase e de iostream
construtor recebe um argumento char* (nome):
class ifstream : public fstreambase, public istream {
Todas as classes incorporam um método
ifstream(const char *nome, int mode=ios::in, int
open() com a mesma assinatura do
prot=0664) : fstreambase(nome, mode, prot); construtor
...
Método close() definido na classe base
}
(virtual) ios
modos alternativos de abertura
segundo argumento com valores de constantes
definidas em ios (ios::binary)
Arquivo seqüencial de saída: ofstream
construtor recebe um argumento char* (nome)
class ofstream : public fstreambase, public ostream {
ofstream(const char *nome, int mode=ios::out, int
prot=0664) : fstreambase(nome, mode, prot); Templates
...
}
segundo argumento com valores de constantes
definidas em ios
ios::binary, ios::nocreate, ios::ate, ios::trunc
O que é um template? Motivação
Mecanismo específico de C++ para Considere a definição de uma classe
definição de classes e algoritmos que ArrayInt que verifica se índice de acesso
manipulem qualquer tipo está entre limites válidos
originalmente, baseado em macros e pré− internamente mantém um arranjo de inteiros
processamento tamanho do arranjo definido no construtor
posteriormente, incorporado ao compilador método size() retorna tamanho do arranjo
de C++
operador de indexação [ ] sobrecarregado
Reuso de código−fonte
classes ou funções
Hierarquia de classes com raiz comum class ArrayInt {
(Object) int* A;
Smalltalk, Java
const int limite;
Estruturas de dados e algoritmos
public:
genéricos manipulam objetos da classe
raiz ArrayInt(int n=100);
uso de polimorfismo int& operator[](int index);
manipulação do tipo efetivo do objeto em int size() { return limite; }
tempo de execução
};
Métodos de ArrayInt Uso de ArrayInt
int main() {
ArrayInt::ArrayInt(int n) : limite(n) { ArrayInt ia(20);
try {
A = new int[n]; for (int i=0; i<ia.size(); ++i) ia[i] = i+1;
cout << "ia[5] = " << ia[5] << endl;
}
cout << ia[35] = " << ia[35] << endl;
int& ArrayInt::operator[](int index) { }
catch(IndexOutOfBounds* iof) {
if (index < 0 || index >= limite) cout << Acesso a posicao " << iof−>pos() <<
}
usar o mecanismo de template
int pos() { return index; }
int max() { return limite; }
};
Definição de template de classes Métodos da classe parametrizada
Definição da classe é precedida pela Método size( ) foi definido inline
palavra−chave template nenhuma indicação especial foi necessária
Após a palavra−chave template, indicação Para os métodos definidos externamente
do tipo de parâmetros para a definição à classe, é preciso indicar que definição é
entre < e > de classe parametrizada
Segue a definição da classe, usando o
mesmo que parâmetro tipo não esteja sendo
parâmetro indicado no lugar do tipo
utilizado no método
variável
Array<int> ia(20); // arranjo de 20 ints Constantes podem ser passadas como
Array<double> da(30); // arranjo de 30 doubles argumentos para template
try {
template<class T, int limite=100>
for (int i=0; i<ia.size(); ++i) da[i] = ia[i] = i+1;
cout << ia[35] = " << ia[35] << endl;
class Array {
} T A[limite];
catch(IndexOutOfBounds* iof) { public:
cout << Acesso a posicao " << iof−>pos() << T& operator[](int index);
" em array de "<< iof−>max() << " posicoes.
<< endl; int size() { return limite; }
} };
}
Templates e arquivos de
cabeçalho Constantes em templates
template<class T, int limite>
Definições de classes parametrizadas e T& Array<T,limite>::operator[](int index) {
seu código podem normalmente estar
if (index < 0 || index >= limite)
presentes em um arquivo de cabeçalho
throw new IndexOutOfBounds(index,limite);
código de template não é a definição ainda,
que ocorrerá com a instanciação do template return A[index];
no código da aplicação }
pode haver necessidades especiais em int main() {
colocar as definições de código de template Array<int,20> ia;
em arquivos fonte; checar compilador
Array<double,30> da;
...
Templates de funções Uso das funções parametrizadas
Definição de métodos de uma classe Seleção da função instanciada ocorre de
parametrizada especifica uma família de acordo com os tipos dos argumentos da
métodos função
um membro para cada instanciação do Exemplo 1: swap de inteiros
template int i=10, j=11;
Mesmo mecanismo pode ser aplicado a swap(i,j);
funções globais com corpo similar exceto Exemplo 2: swap de complexos
pelos tipos manipulados
complex<double> c(1,1), h(2,2);
funções parametrizadas swap(c,h);
Exemplo C++ Standard Template Library
int main( ) { conjunto de classes e algoritmos
int i=10; parametrizados
containers
double d=21;
algoritmos genéricos
swap(i,d);
iterators
} objetos função
adaptadores
Erro: não é possível gerar swap(int,double); alocadores
biblioteca de templates
padrões Dois tipos de containers em STL
containers de seqüências
(STL)
containers associativos ordenados
Containers de seqüências deque<T>
Organizam uma coleção de objetos de Oferece acesso direto a uma seqüência
mesmo tipo em uma estrutura linear de de comprimento variável
tamanho variável tempo de leitura de um elemento em
Podem ser de três tipos: qualquer posição não depende da posição
vector<T> Tempo de inserção e de remoção de
deque<T> elementos é constante para o início e para
list<T> o final da seqüência
vector<T> list<T>
tempo de leitura de um elemento em comprimento da seqüência
Tempo de inserção e de remoção de elementos é constante para qualquer
elementos é constante apenas para o final posição da seqüência
da seqüência
Containers associativos ordenados map<Key, T>, multimap<Key, T>
Definem coleções de objetos de tamanho map<Key, T>
variável com rápida recuperação baseada permite a recuperação rápida de um tipo T
em valores de chaves com base no valor da chave de tipo Key
set<Key> sem repetição de valores de chaves
multiset<Key> multimap<Key, T>
map<Key, T> como map<Key, T>, porém permitindo a
multimap<Key, T> replicação de valores de chaves
multiset<Key> arranjos e strings
rápido tempo de recuperação
Exemplos de algoritmos genéricos Exemplos de algoritmos genéricos
fill search
preenche um trecho de uma coleção com o tenta localizar a ocorrência de uma faixa de
mesmo valor repetido múltiplas vezes valores em uma faixa da coleção
generate min_element
preenche um trecho de uma coleção com o localiza o menor valor em uma faixa da
valor de retorno de uma função especificada coleção
count max_element
produz o número de elementos que, em um localiza o maior valor em uma faixa da
trecho da coleção, satisfaz um predicado coleção
copy replace
copia trechos de uma coleção especificada varre uma faixa da coleção trocando o valor
para outra coleção especificado (se ocorrer) pelo novo valor
copy_backward replace_if
predicado especificado ser verdadeiro para o
reverse
valor armazenado na coleção
equal
rotate
verifica se duas faixas têm os mesmos
troca dois trechos de uma coleção valores na mesma ordem
Exemplos de algoritmos genéricos Iteradores
remove Base para operação dos algoritmos
rearranja a coleção de forma que os
genéricos
elementos removidos estejam no final,
retornando a indicação da nova posição final oferecem referências a posições nas
coleções
unique
Cinco categorias
remove elementos duplicados da coleção
Input iterators
sort, stable_sort Output iterators
ordenam elementos na faixa especificada da Forward iterators
coleção
Bidirectional iterators
Random access iterators
coleção
igualdade: ==, !=
Output iterator
Forward iterator Componente que modifica a interface de
avanço: ++ (forma pré−fixa e pós−fixa) outro componente
Bidirectional iterator exemplos:
reverse_iterator, para inverter a ordem de
engloba forward iterator varredura de um iterador
retrocesso: −− (forma pré−fixa e pós−fixa) stack_adaptor ou queue_adaptor, para restringir a
política de acesso a um container
Random access iterator
binder, para converter um objeto função de binário
engloba bidirectional iterator para unário fixando o valor de um dos operandos
+, − , +=, −=, <, >, <=, >=
Funções que podem ser passadas como Classes associadas a containers com o
argumentos para os algoritmos genéricos objetivo de isolar os detalhes internos de
especificando operações binárias, predicados armazenamento
Genericamente, pode ser também manipulação de ponteiros e referências é
oculta pelos métodos dos alocadores
Objetos com C++
Prática
não se domina uma linguagem de
programação na teoria
Comentários finais
Domínio da linguagem de programação
não garante sucesso de desenvolvimento
aplicação de técnicas sólidas de projeto e de
implementação
design patterns, extreme programming
trabalho em equipes
com forte gerência de projetos
Tempo de desenvolvimento de aplicações
relativamente longo
Uso eficiente de memória
metade do uso de código equivalente em
linguagem script, um terço de Java
Execução mais rápida
cerca de duas vezes mais rápido que Java
Fonte: An empirical comparison of seven programming
languages (Lutz Prechelt), IEEE Computer, October
2000