Obra: Programar em C++/Classes
classes existem duas categorias de tipos de dados usuais em são classificados como tipos básicos tipos definidos pelo programador assim como na linguagem podemos definir dados compostos por associações dos tipos básicos estes tipos são chamados de estruturas structs traz uma nova representação de dados muito semelhante na forma às estruturas porém diferentes na forma conceitual palavra chave class que usada para criar uma classe de objetos mais rica que as structs ao declararmos um identificador tal qual fazemos ao declarar uma variável no lugar do tipo especifiquemos uma classe criaremos um objeto antes de prosseguirmos vejamos um pouco sobre conceito por trás do uso de objetos um objeto entendido como uma entidade de dados dentro da memória que basicamente deve ser responsável por seu conteúdo ou seja um objeto deve ser capaz de gerenciar seu conteúdo autonomamente ou prover meios de outras entidades de código fazê lo de forma segura origem atributos observemos por exemplo código abaixo struct mydata int char data float nreal esta declaração bem conhecida de quem já está familiarizado com linguagem cria um tipo de dado composto heterogêneo que neste exemplo chamamos de mydata que acontece aqui que os dados estão agrupados dentro desta estrutura isto promove possibilidade de manipulá los em conjunto um dos problemas com esta estrutura presença de uma matriz de caracteres chamada data observe que mesma tem um tamanho definido de caracteres imagine que em algum momento da execução do programa tentamos colocar um caractere na posição ou qualquer posição fora da matriz neste caso estamos colocando referido dado em endereços inválidos para operação que pretendemos realizar ou seja não há controle nenhum que assegure que código não fará um acesso fora da área que pertença matriz um acesso de memória qualquer elemento da matriz acima da posição fará com que invadamos dados na área onde variável nreal está definida funções membro métodos agora suponha que tenhamos como definir um modo para entrar outro para ler dados da matriz struct mydata int char data float nreal bool write_data int pos char if pos pos pos agora temos assegurados métodos de inclusão acesso dados da matriz de caracteres porém ainda existe um pequeno problema quem quiser antigo método de acesso direto conseguirá facilmente pois os elementos da estrutura estão acessíveis publicamente por padrão conceituação problema da visibilidade pública dos dados em uma estrutura pode ser resolvido com um dos conceitos de objetos encapsulamento encapsular os dados significa reservar acesso funções que estejam dentro de um grupo restrito especializado para tais operações de manipulação destes dados uma das vantagens deste procedimento que código adquire um formato mais organizado onde os processos tornam se claramente distintos caso tenhamos que analisar código cada procedimento estará restrito partes definidas para cada operação declarando classes as estruturas são bem parecidas com as classes com um
efinidas para cada operação declarando classes as estruturas são bem parecidas com as classes com uma pequena diferença peguemos caso da passagem de estruturas como argumentos de funções include include using namespace std class person string name int height void setvalues person void getvalues const person int main person setvalues cout pers height cin ignore void getvalues const person pers cout mudamos identificador de struct para class mas se tentarmos compilar programa isto vai causar erros de compilação porque agora temos variáveis membro que são privadas por padrão estas não são vistas por funções fora da classe dentro de uma classe podemos definir diversos modos de visibilidade de variáveis funções as modalidades podem ser private só podem ser acessados por membros da mesma classe public pode ser acessadas fora do objeto onde este estiver definido protected deixemos esta para quando falarmos em classes derivadas pois depende deste conceito ora como as funções getvalues setvalues não são membros da classe person tal como construtor person não conseguem acessar as variáveis name height visualizamos melhor em forma de tabela class person private string name int height solução criar funções publicas para ler de escrever para as variáveis privadas include include using namespace std class person private string name int height public string getname const void setname string int getheight const void setheight int string person getname const return name void person setname string if length name no name assigned else name int person getheight const return height void person setheight int if cin ignore pers setheight void getvalues const person pers cout mas perguntam por que que nos demos ao trabalho de recorrer membros privados em vez de fazer todos públicos quando tínhamos uma estrutura no lugar de uma classe não havia nada que impedisse colocação de valores inválidos por isso poderíamos ter valores vazios para string valores negativos para variável height agora que person uma classe as funções membro podem realizar validação dos dados antes da atribuição de valores nas variáveis poderíamos fazer com que função setname verificasse se entrada na string seria vazia caso fosse colocaria um valor padrão como sem nome similarmente poderíamos ter setheight para verificar se seriam colocados valores de entrada negativos caso fossem colocaria zero ou não tomaria nenhuma ação todas estas características demonstram conceito de encapsulamento sua finalidade de tornar código mais modularizado restringindo escopo de análise partes bem delimitadas dos programas devido este conceito podemos contar com códigos mais fáceis de analisar fazer manutenção instanciando objetos instanciação de objetos processo de criar estrutura lógica dos mesmos na memória isto ocorre quando declaramos os objetos pois neste momento todo processo de construção dos mesmos efetivado assim toda vez que declaramos um objeto estamos instanciando ou seja estamos criando uma instância da cla
toda vez que declaramos um objeto estamos instanciando ou seja estamos criando uma instância da classe podemos declarar os objetos logo após definir classe conforme podemos ver no caso logo abaixo neste caso teremos variável rect criada como um objeto conforme estabelecido pelo modelo definido pela palavra chave class este tipo de declaração mais usual para objetos criados globalmente pois inclusão desta declaração no cabeçalho pode fazer com que vários objetos sejam criados com mesmo nome quando cabeçalho invocado de vários arquivos portanto mais prudente usar esta opção quando declaração está no arquivo fonte não no cabeçalho caso class crectangle int public void set_values int int int area void rect no caso apresentado logo abaixo podemos declarar objetos apenas quando precisarmos esta opção de declarar objeto depois mais usada pois na maioria das vezes temos modelo dos objetos classe declarada em um arquivo de cabeçalho enquanto que os objetos serão criados no resto do código fonte desta forma mais usual criar as classes em cabeçalhos depois declarar os objetos na parte do programa que for mais conveniente caso class crectangle int public void set_values int int int area void int main crectangle rect em ambos os casos temos crectangle private public int int void set_values int int int area void rect podemos então entender os objetos como blocos de dados que têm propriedades variáveis que podem fazer algo métodos então criamos todas as funcionalidades que precisamos que classe forneça aos programas fazendo os testes necessários para assegurar sua consistência estabilidade sempre que precisemos utilizar os objetos só temos que instanciá los declará los não precisamos nos preocupar como eles funcionam internamente uma vez que os desenhamos adequadamente para entendermos melhor este conceito podemos fazer uma analogia consideremos um objeto resistência sabemos que temos de usá lo que ela deve ter certas características então teremos seu valor em ohms sua potência máxima tolerância entre outras teremos uma função que nos dará corrente que passa por ela quando lhe aplicamos uma tensão elétrica não precisamos saber de que que ela feita ou como estas características internas faz funcionar basta nos receber os resultados vejamos exemplo agora vamos mostrar que podemos ter funções membro apenas como protótipos defini las fora da classe para isso usamos operador de definição de escopo que permite definir local do código onde um identificador existe no formato escopo função ou escopo dado de maneira geral quando declaramos identificadores dentro da classe podemos defini los no escopo global referenciando estes pelo operador de escopo classes example include using namespace std class crectangle int public void set_values int int int area return void crectangle set_values int int repare no que pemite nos definir função membro da classe crectangle fora da classe int main crectangle rect definimos objeto de classe rect set_values objeto membro cout using na
asse int main crectangle rect definimos objeto de classe rect set_values objeto membro cout using namespace std class crectangle int public void set_values int int int area return int main crectangle rect definimos objeto de classe rect set_values objeto membro cout area exemplo anterior explora característica de toda figura geométrica fechada que possui uma área interna observe que este modo de definir classe coloca cálculo da área dentro da definição da mesma este modo faz com que código seja apenas um modelo função de cálculo da área não será criada se não for usada durante escrita do resto do programa vejamos outro exemplo class dog public void setage int age int getage void setweight int weight int getweight void speak private int age int weight void dog setage int age this age age int dog getage return age void dog setweight int weight this weight weight int dog getweight return weight void dog speak cout acima podemos ver um modo de declarar as funções apenas como protótipos que ficam dentro da declaração da classe nesse contexto as funções são definidas fora da classe usando se operador para ligar função classe neste caso teremos as funções definidas construídas no código das mesmas enquanto que modelo da classe poderá permanecer em um arquivo cabeçalho que possibilita incluí lo em qualquer arquivo de códigos fontes do programa definição de classes usa se palavra class para criar uma classe seguindo se depois nome que se queira dar lhe finalmente definição da mesma entre chaves definição contém os dados propriedades os métodos as funções membro vamos acompanhar com um exemplo vamos fazer desenho de uma classe chamada image que será usada para guardar manipular uma imagem primeiro perguntamos que necessário para guardar uma imagem depois que tipo de manipulações necessitamos imagem possui pixels de largura pixels altura cada pixel tem as propriedades de cor imagem cor composta por vermelho azul verde numa escala de portanto vamos necessitar de membros para guardar estas informações agora planejemos os métodos vamos primeiramente assumir que temos restrição de class image public int getwidth int getheight void setx int int getx void sety int int gety void setred double red double getred void setblue double blue double getblue void setgreen double green double getgreen private int int int int double double double boolean iswithinsize int double clipintensity double brightness especificadores de acesso temos as palavras reservadas private public são os chamados especificadores de acesso private especifica uma faixa de variáveis ou funções que podem ser acessadas exclusivamente por membros da classe de forma que nenhum outro código fora da mesma possa acessá las public especifica uma faixa de variáveis ou funções que podem ser acessadas por qualquer código no programa sendo que para as funções internas da classe não necessário especificar objeto enquanto que para as outras partes do programa necessário especificar objeto qual estas pertencem e
enquanto que para as outras partes do programa necessário especificar objeto qual estas pertencem esta característica de limitar acesso manipulação dos membros de uma classe chama se encapsulamento boa prática no desenho de classes deve sempre forçar encapsulamento raramente necessário ou desejável ter acesso livre público aos dados internos de uma classe encapsulamento visa primariamente duas metas eliminar necessidade de conhecimento da estrutura interna por quem deseja usá la por exemplo se os objetos precisam manter um conjunto de bytes isto pode ser conseguido usando se duas variáveis short int uma int um vetor com characteres ou variação de qualquer um dos anteriores sem sinal mas estes detalhes não precisam estar expostos se representação interna dos dados for modificada desde que os tipos de retorno de parâmetros das funções públicas mantenham se inalteradas não necessitemos de alterar código que utilizem objetos da classe ou seja encapsulamento simplifica programação escondendo as da classe elimina retrabalho do código por alterações da mesma geralmente as funções métodos privadas são auxiliares outras funções da classe se nenhum especificador de acesso for usado todos os membros metodos são declarados como privados por padrão há dois métodos para definir as funções membro eles podem ser definidos dentro da classe que apropriado para funções pequenas funções grandes podem ser definidas fora da classe neste caso terão de ser identificadas como pertencentes classe para isso utilizamos operador de resolução de escopo construtores conceito os construtores constructors são funções membro métodos especiais de uma classe permitem inicialização das variáveis membro de um objeto ou melhor permitem construção inicialização de objetos das classes se não os declararmos compilador faz isso por nós os construtores têm sempre mesmo nome que classe os objetos são construídos através destas funções especiais chamadas de construtores até aqui não os declaramos eram criados automaticamente estas funções tem certas características que as fazem distintas das normais que permitem que as mesmas construam estrutura lógica inicial do objeto desta forma estas funções são características da orientação objetos servem para criação dos mesmos construtores não podem ser chamados explicitamente como fazemos no caso de funções membro regulares eles são apenas executados quando um novo objeto da classe criado portanto existe apenas um evento capaz de executar um construtor instanciação do objeto as principais características dos construtores são não têm qualquer valor de retorno não podem ser executados por chamada explícita no código são executados logo depois que os tipos básicos do objeto foram criados inicializam os dados com os valores que objeto precisa para começar funcionar corretamente declaração podemos criar construtores facilmente através das características que os distinguem das funções membro convencionais ou seja definimos uma função membro que possua mes
ue os distinguem das funções membro convencionais ou seja definimos uma função membro que possua mesmo nome da classe não tenha tipo de retorno declaramos como pública para que possa ser acessada por quem queira instanciar objetos vejamos como definir um construtor class caneta string cor int volume public caneta string int caneta caneta string int cor volume construtores podem iniciar os membros da classe de uma forma simplificada este formato usado sempre que construtor tem dados básicos que podem ser iniciados antes do resto da construção da estrutura do objeto podemos iniciar os dados do objeto declarando desta forma class caneta string cor int volume public caneta string int cor volume para fazê lo como vemos no código basta listar as variáveis membro em uma sequência depois da declaração do nome do construtor de um sinal de dois pontos iniciamos uma lista de membros com valor ser atribuído entre parênteses depois de cada um separando os por vírgulas destrutores conceito além do construtor linguagem assim como outras linguagens orientadas objeto possuem outro tipo de função especialmente criada gerenciada pela linguagem os destrutores estas são destinadas desmontar estrutura do objeto quando mesmo está sendo encerrado destrutor terá mesmo nome da classe mas precedido pelo sinal til também não retorna valor destrutor tem as seguintes características destrutor chamado quando objeto está sendo finalizado usado para liberar qualquer memória que tenha sido alocada declaração façamos classe dog com construtor destrutor class dog public dog constructor dog destructor void setage int age int getage void setweight int weight int getweight void speak private int age int weight dog dog age weight cout repare que construtor tem mesmo nome que classe destrutor tem mesmo nome que classe com prefixo de tilde construtor foi usado para inicializar as variáveis membro mas noutros exemplos poderia alocar memória tomar controle de recursos como dispositivos de sistema executar inicializações de código destrutor no exemplo não faz nenhuma ação real para além de fazer eco informando que foi chamado copy constructors um copy constructor um construtor especial que toma como argumento referência de um objeto da mesma classe cria um novo objeto que copia do objeto em referência por padrão compilador providencia um copy constructor que faz cópia membro por membro do objeto original construindo um objeto idêntico isto chamado de shallow copy ou member wise em algumas situações cópia de um objeto não satisfatória para ver isso vamos ver classe employee abaixo include include using namespace std class employee public employee char name int id employee char getname return private other accessor methods int char employee employee char name int id id new charstrlen name allocates an character array object strcpy name employee employee delete int main employee programmer john cout função strlen retorna tamanho da string passada pelo constructor repare que nome do employee ag
cout função strlen retorna tamanho da string passada pelo constructor repare que nome do employee agora guardado num carácter array dinâmico string lenght para permitir null terminator usado no estilo função strcpy automaticamente adiciona null terminator string destino note também que destrutor liberta memoria usada para guardar employee name para evitar memory leak agora imagine que john promovido int main employee programmer john cout este programa contém um bug sério morre com uma exceção quando executado problema que construtor que está sendo usado para criar um objeto manager mas ele copia endereço no ponteiro em manager nos temos pointers ambos contendo mesmo endereço imagine que agora um novo empregado contratado quando nome for atualizado não apenas iremos alterar nome do empregado mas também do gerente finalmente quando os objetos deixarem de ser usados destrutor da classe fizer liberação de espaço na memória tentará liberar duas vezes para mesmo endereço provocando um erro no sistema de alocação dinâmica de memória que forçará sistema operacional eliminar programa da memória para resolver esse problema podemos definir um construtor de cópia copy constructor na classe substituindo implementação padrão do compilador este recurso automaticamente identificado faz com que compilador não crie sua versão do construtor assim definir um construtor de cópia próprio maneira mais eficiente quando nossos objetos detêm características que os fazem diferentes do padrão para criar construtor basta definí lo na classe class employee public employee const employee employee char name int id employee char getname return private other accessor methods int char employee employee const employee new charstrlen allocates an character array object strcpy agora temos um construtor que pode fazer cópia da forma correta neste novo construtor alocamos uma cadeia de caracteres para copiar conteúdo da original no objeto ser copiado copiamos conteúdo para novo objeto assim teremos objetos distintos cada um com seu próprio conteúdo ver também programação orientada objetos classes objetos conceitos teóricos de programmierung klassen