Legal.
Aqui dá pra gente fazer algumas reflexões, tá, sobre o que foi feito.
Inclusive, já no tocante à relação entre objetos.
Aí, você já tem um pequeno exemplo
um exemplo inicial, bem entendido.
A gente vai melhorar esse conceito na sequência.
Mas ele é um exemplo de agregação forte, ou de composição.
Se você olhar do ponto de vista de objeto, o que acontece
é que você tem um ObjetoPrincipal,
você tem um sistema, você tem um mundo, portanto.
E dentro desse ObjetoPrincipal aqui, o seu mundo,
você tem, dentro dele
O Simao, o Einstein e o Newton.
Até digo mais, você tem o Simao, o Einstein e o Newton
e você também tem
o diaAtual, o mesAtual
e o anoAtual que são objetos
da classe int, uma classe pré-existente.
Agora, um int, não deixa de ser uma classe
o diaAtual, mesAtual e o anoAtual, não deixa de ser
um objeto, ainda que mais singelo, se a gente quiser olhar assim.
A classe Principal
define que seus objetos devem ter um relacionamento
com três objetos provenientes
instanciados da classe Pessoa.
Ao bem da verdade,
estes três objetos estão incluídos
ou agregados no objeto da classe Principal.
Aqui é 'no', né, não é...
'em cada', porque só tem um,
e, em via de regra, só vai ter um ObjetoPrincipal.
Ok.
Avancemos aqui um slide.
Nesse código em questão,
quando um objeto da classe Principal é criado,
são criados três objetos da classe Pessoa,
nomeadamente Simao, Einstein e Newton.
Segundo este código,
quando cada objeto da classe Pessoa é criado,
é automaticamente chamado seu construtor sem parâmetro
Isso é interessante.
Vocês tão vendo aqui na construtora, aqui, ó,
do Principal.
Principal.h e Principal.cpp.
Você não tá chamando a construtora em lugar nenhum.
Quando você não chama a construtora explicitamente,
implicitamente o C++ dá um jeito de chamar a construtora.
O C++ quando vocês...
quando você não chama a construtora,
ele tem que chamar uma construtora sem parâmetros,
ou equivalente.
É por isso que ele cria
uma construtora sem parâmetros se você não fizer
uma construtora que é sem parâmetros ou equivalente.
Eu não expliquei ainda o que é equivalente, vou explicar em outro momento dado.
Normalmente é dentro dessa exposição corrente.
Ok.
Aqui, então, a hora que eu crio
o ObjetoPrincipal de Principal
acontece o que tá descrito aqui, e o que eu já li.
Legal. Avancemos mais um slide,
que a gente vai destacar essa função main aqui no próximo slide.
Ali a função main.
Quando eu crio o ObjetoPrincipal, ele cria o ObjetoPrincipal,
mas antes dele rodar
a sua própria construtora,
ele vai criar o objeto Simao da classe Pessoa
e chamar a construtora sem parâmetros pro Simao
que zera tudo,
no tocante ao Simao.
Vai criar o objeto Einstein da classe Pessoa,
chamando a construtora sem parâmetros pro Einstein
que zera tudo, no tocante ao Einstein,
os atributos do Einstein.
Vai chamar
aliás, vai criar, aí, o objeto Newton da classe Pessoa
chamando a construtora sem parâmetros pro Newton,
zerando todos os atributos do Newton, tá.
Digo mais até,
ele vai chamar o...
a construtora...
aliás, ele vai criar o objeto diaAtual,
chamar a construtora desse inteiro,
porque ele tem uma construtora,
muito embora a gente não esteja vendo.
Vai criar o objeto mesAtual,
chamar a construtora.
Vai chamar o objeto anoAtual,
objeto da classe inteiro,
e chamar a construtora desse anoAtual.
Só daí, então,
ele criou todos os seus atributos
dentre os seus objetos, que são atributos nesse caso
chamou suas construtoras
que ele vai, por fim,
chamar a sua própria construtora.
Por isso tá escrito aqui: "Chama a construtora do ObjetoPrincipal".
Então, você tem que ter um pouco noção, ciência,
desse protocolo, aí, né, desse rito
de inicialização das coisas
pra não se perder, tá.
As vezes, você acha que antes ele executa
a construtora do ObjetoPrincipal pra depois executar
a construtora dos
atributos, ou seja, dos objetos agregados nesse caso.
Então, não. Ele chama primeiro dos objetos que ele tá criando
na forma de atributos, eles são objetos, né.
Simao, Einstein e Newton como no caso exposto.
Depois que ele vai chamar a sua própria construtora.
Atenção no próximo slide, tá,
tem uma confusão que muita gente faz quando programa em C++.
Tentar fazer esse tipo de coisa aqui, ó.
Chamar, ali no âmbito da Principal,
a construtora de Pessoa.
Isso não funciona.
Professor, mas assim que se fazia na função main
lá à hora que você tava fazendo as primeiras versões do programa.
Vejam a diferença, pessoal.
Quando eu criei o Simao dentro da main,
eu tava criando o Simao dentro deu uma função.
Aqui eu não estou criando o Simao dentro de alguma função,
eu estou criando o Simao dentro de alguma outra classe
que vai gerar um objeto,
ObjetoPrincipal, no caso.
Quando eu ponho um... crio um objeto dentro de outro objeto,
eu não posso chamar a construtora assim.
Não funciona, dá erro.
Até seria bonito se pudesse.
Até eu não acho uma...
sintaxe ruim, pelo contrário,
mas não é assim que a gramática da linguagem permite.
E como a gramática da linguagem permite?
Permite o que segue, ó.
Isso sim é possível.
Lá no âmbito da implementação da construtora
do Principal.cpp,
eu largo o dois pontos aqui, ó,
e aí, chamo aqui o objeto,
boto atrás o seu nome,
e aí, sim, passo a parametragem correta.
Isso sim funciona.
Se eu tô chamando explicitamente a construtora com parâmetros,
que já vai chamar a inicializa, passando esses parâmetros,
aí eu não preciso disso e comento aqui.
Professor, mas eu quero chamar também a construtora do Einstein e do Newton,
Como é que eu faço?
É assim, ó.
E aí, eu comentei aqui.
Nesse quadro que tá exposto em tela,
aqui nas reticências tá aquela questão lá do
time.h ou do Windows.h, tá, pra recuperar
a data atual, enfim, o dia atual, mês atual e ano atual do sistema, tá.
Bem entendido.
Mas o que interessa pra gente aqui é isso
que a gente tá expondo aqui
até por isso que isso aqui ficou em reticências.
Gente, aqui eu tô chamando explicitamente, esse é um código muito bonito.
E eu chamando explicitamente a construtora,
daí eu não precisaria dessa função... dessa chamada de função inicializa() aqui,
tanto é que tá comentado.
Inclusive, à rigor, a função inicializa, lá na Pessoa
até poderia ser uma função privada.
Professor, eu posso ter função privada? Pode.
Uma função privada, é uma função
que só vai ser chamada por outras funções públicas,
mas essa função mesmo não é chamada de fora
do âmbito da classe,
ou do objeto dentro da classe.
Ok. Então, isso é interessante, tá.
Bom, mas eu prefiro chamar o inicializa,
por algum motivo eu não quero usar a construtora nesse momento,
e eu quero chamar a inicializa.
Ok.
Se existe um motivo pra isso, suponho que exista, tá.
Daí, tem outra maneira de fazer.
Essa aqui, ó.
Aqui, então, eu tô chamando,
explicitamente,
as construtoras sem parâmetro.
Vejam que a última fica sem vírgula aqui, ó.
Nas anteriores têm vírgula.
Já era assim no slide anterior, tá.
Vírgula, vírgula, sem vírgula.
Legal.
Então, aqui, eu tô chamando explicitamente,
nesse slide 31,
as construtoras sem parâmetro.
O que é interessante, porque eu tô explicitando.
Se eu tirasse todo esse código que tá em marrom aqui,
marrom-claro aqui.
Se eu tirar esse código,
bom, eu não sou muito com cores, tá, mas é algo assim.
Se eu tirar esse código aqui das construtoras sem parâmetro,
e um dos dois pontos
ele vai fazer isso aqui automaticamente pra gente.
Se a gente não explicita, ele faz isso automaticamente.
Então, C++
se você não criar a construtora sem parâmetro pra ele
ele cria automaticamente pra você,
o que tem perigo,
e se você não chamar explicitamente a construtora sem parâmetro,
ele chama pra você.
Meu conselho
é que sempre chame as construtoras explicitamente
pra você lembrar onde você tá chamando
e também pra outros...
elementos da equipe
de desenvolvimento trabalhando com você
em um dado software
terem noção do que você tá fazendo.
Bom, aqui, então, a Principal
ela só tem uma construtora
que gera uma construtora sem parâmetro
e tá legal assim.
Ela respeita, então, aquela regra nossa de
criar explicitamente a construtora sem parâmetro.
E pra ela só precisa dessa construtora, então tá tudo em ordem.
Outra coisa que é importante a pessoa ter,
ali numa construtora sem parâmetro,
é uma destrutora.
Uma função destrutora
é o inverso de uma função construtora.
Função destrutora ela tem o mesmo nome da classe,
precedido de um til aqui, ó.
Se a construtora
inicializa elementos como atributos,
e também serve pra alocar memória
quando for o caso. Não é o caso aqui.
Por sua vez, a destrutora zera alguns elementos,
ela serve, nem que não seja o caso aqui
pra desalocar memória
quando alguma memória tenha sido alocada
em alguma função-membro do objeto.
Isso é verdade.
Imaginem que, em algum momento,
na construtora ou no Executar() eu tivesse feito um malloc, por exemplo.
Memory allocation lá da linguagem C.
Seria no âmbito da destrutora, ao mais tardar,
que eu faria esse free.
E tem outras atividades também que são criadas no âmbito de uma destrutora.
Por exemplo, um fechamento de arquivos quando você tá trabalhando com arquivos,
e assim vai.
Destrutora, gente, não tem parâmetros por definição.
Se colocar parâmetro nisso daqui, dá erro.
Então, não tem parâmetros.
E adivinhem, se você não criar uma destrutora assim...
bom, aliás, se você não criar uma destrutora,
que por definição é sem parâmetro,
o C++ cria pra você.
No exemplo em questão,
a destrutora é vazia
e serve pra atender a regra geral
que toda classe deve ter uma destrutora vazia.
Na verdade, toda classe deve ter uma construtora
e uma destrutora
uma construtora sem parâmetros com algum código,
e uma destrutora, agora sim, vazia,
para impedir que o compilador, em alguns casos,
as crie automaticamente,
fazendo com que o programador/desenvolvedor não controle isso.
Então, uma construtora aqui... tô explicando melhor aqui, tá.
a construtora sem parâmetros
inicializando apropriadamente os atributos,
e daí, uma destrutora, vazia vazia, no sentido que
nesse exemplo, aqui, tá, é vazio, ó. Vazia.
Mesmo que não faça nada, ele tem que existir.
pra você saber que, justamente,
ele não tá fazendo nada. Você tá vendo que ele tá ali e não faz nada,
não deixa isso a cargo da linguagem, compilador.
Bom, vocês sabem que esses exemplos que eu tô rodando aqui,
eu tô rodando todos eles em Microsoft Visual C++,
que agora eles chamam lá de
Visual Studio, né, Visual Studio 2019, tá.
Comporta inclusive a linguagem C++.
Se você pegar alguns ambientes
um pouco mais completos que o
Visual Studio, daí vem com C++, C#, vai ter outras coisas juntos, tá.
Nos concentremos aqui no C++.
Então, tô fazendo esses códigos, aí
no Visual C++, e aí, entra essa observação técnica.
Num primeiro momento,
esses códigos tinham sido feitos em Dev C++,
mas esse Dev C++ era um pouco instável em algumas situações,
aí depois eu fiz o que eu indiquei pros alunos aqui, tá.
Imigrei os códigos do Dev C++
pro Express Edition.
O Express Edition, tem algumas peculiaridades.
aqui eu vou salientar uma peculiaridade dele.
Uma ou duas.
Bom, aqui vocês tão vendo a classe Pessoa, que a gente vêm trabalhando aí.
O .h e o .cpp e reticências ali.
Aqui tá o Principal. Principal.h e Principal.cpp.
Aqui tá o main. No ambiente Microsoft,
conforme você cria o projeto,
em vez de criar a função main, ele vai criar underscore _tmain.
Esses argumentos aqui, argc, argv,
que são coisas que eu suponho que vocês tenham estudado em fundamentos 1.
Se não estudaram, entrem no grupo de slides lá
de fundamentos 1 que em algum momento aparece lá.
Muito bem.
Além de ele ter os arquivos que a gente espera que ele tenha,
que vai ser o Pessoa.h, o Pessoa.cpp,
o Principal.h, Principal.cpp,
e algo como ProjetoOO.cpp
que é o equivalente ao main.cpp em outros compiladores, tá.
Ele tem aqui o nome do projeto no menu principal.
Ele vai ter mais dois arquivos,
que é o standard affixment (stdafx.h)
e o respectivo .cpp.
Dizem que pra todo .h tem que ter um .cpp
então eles mantiveram isso aí.
Pra quê mesmo que serve o .h...
Isso aqui? Esse stdafx eu já discuti em aula passada, tá.
Eu vou lembrar aqui.
É basicamente uma maneira do...
desse ambiente Microsoft aí, concentrar a inclusão de bibliotecas externas.
Deixe-me voltar aqui...
Se eu pegar aqui, sei lá,
essa versão 2G ali.
Vou abrir ela aqui no .sln, direto.
Muito embora, eu prefiro abrir a partir do próprio ambiente.
Tá perguntando aqui se eu quero converter as coisas. Já sabe que a gente tem que converter.
Se eu olhar lá, por exemplo,
o Pessoa.cpp.
Ele usa o cout,
mas você não tá vendo aqui o #include .
Você tá vendo o #include "Pessoa.h", o que é natural,
porque o Pessoa.cpp tem que incluir o Pessoa.h, né, pra implementar.
E tem uma outra inclusão aqui que é o stdafx.h.
Tá. Mas o que que é isso?
Aí, você vai verificar lá,
e você vai entender
por definição
que, na verdade, esse stdafx aqui
é um concentrador de bibliotecas externas.
Aqui, ó, adicione os headers
as bibliotecas, né, os cabeçalhos externos.
Então, aqui, por exemplo,
incluir aqui o iostream
usando aí o cout, endl e o cin.
Aqui eu já tenho o stdio,
tenho o tchar.h, tenho o string.h.
Agora, cada vez que eu precisar usar, em algum momento, usar alguma biblioteca externa,
em vez de eu ficar escolhendo as bibliotecas, tá,
eu venho aqui
e simplesmente, por exemplo, aqui ó, no Principal.cpp
é outro lugar que eu vou precisar utilizar o cout/cin.
Ali, ó.
Nessa versão que eu abri aqui, tá.
Nas versões que a gente já trabalhou, aí, nos slides
Ali ó, só incluo o stdafx.
Professor, mas ele não vai fazer repetição de inclusões,
ficar reincluindo stdafx varias vezes?
Ele tem um dispositivo aqui, tá, que a gente vai discutir na sequência,
logo logo, inclusive,
que é o pragma once.
Pragma once resolve essa questão.
Ele garante que, por mais que você faça apelo,
a várias inclusões dessas bibliotecas, tá,
no código gerado, no final das contas,
ele vai incluir num único lugar e compartilhar
entre os elementos que fazem apelo
a esse cara aí do ponto de vista
pra evitar redundâncias
lá no código gerado, bem entendido.
Muito bem, isso aqui foi só pra explicar então esse nuance
do nosso amigo aí, Visual Studio,
Microsoft Visual C++.
Legal.