Nesta primeira aula, teremos uma visão panorâmica de como classes funcionam no geral, tenho certeza que aqui tem bastante informação, mas não se preocupe, nas próximas aulas voltaremos a pontuar os elementos dessa aula. Sem mais delongas, vamos para a aula.
Definição
Programação orientada a objetos (OOP) é um paradigma da programação que usa objetos para representar e manipular dados. Esses objetos são instâncias do que chamamos de classes, ou modelos para objetos e que são responsáveis em definir as propriedades (membros de dados) e métodos (funções) que os objetos de tal classe terão.
Pense que classes são como as Structs que estávamos criando em C, porém, essas Structs agora possuem superpoderes. Classes, além de poder amarrar variáveis, também podem conter funções, além de outras funcionalidades como construtores e funções mágicas, que permitem criar lógica de uma forma mais natural e simplificada. Exemplo de linguagens que usam OOP incluem: Java, Python, C++ e C#.
Structs
Para exemplificar melhor, voltaremos para a Struct "Pessoa" que fizemos aulas atrás, para explicar como uma classe funciona.
C
struct Pessoa {
char nome[50];
int idade;
int altura_em_cm;
float peso_em_kg;
};
Você também deve se lembrar que para instanciar e manipular dados dessa nossa struct, usávamos essa sintaxe:
C
#include<stdio.h>
#include<string.h>
struct Pessoa {
char nome[50];
int idade;
int altura_em_cm;
float peso_em_kg;
};
int main(void) {
struct Pessoa p;
p.idade = 25;
strcpy(p.nome,"Joao");
p.altura_em_cm = 180;
p.peso_em_kg = 75;
printf("Nome: %s\nIdade = %d\nAltura em cm = %d\nPeso (em kg) = %g\n", p.nome, p.idade, p.altura_em_cm, p.peso_em_kg);
}
Vamos supor que agora queremos transformar essa Struct em uma classe, como podemos fazer isso?
Traduzindo a Struct para a Classe
Começaremos então com mais básico necessário para criar essa classe:
#include<iostream>
#include<string>
class Pessoa {
private:
std::string nome;
int idade;
int altura_em_cm;
float peso_em_kg;
};
int main(void) {
std::cout << "Hello world!\n";
// Obs.: Se estivessemos criando essa instancia como ponteiro, nos utilizariamos a palavra "new"
// antes de Pessoa, assim:
// Pessoa* joao = new Pessoa();
Pessoa joao = Pessoa(); // Ou "Pessoa joao();"
return 0;
}
class Pessoa {
private String nome;
private int idade;
private int altura_em_cm;
private double peso_em_kg;
}
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!\n");
Pessoa joao = new Pessoa();
}
}
Até aqui não tem nada de muito novo, nós criamos uma classe, e então instanciamos um objeto da nossa classe, então temos essa variável "joão" que é uma instância da classe "Pessoa". Mas nessa sopa de letrinhas você deve ter notado uma palavra nova: "private".
Escopo de uma classe
Quando estamos trabalhando com classes, nós precisamos definir quais membros da minha classe são acessíveis fora da classe, e para isso temos essas três palavras-chave (mais comuns): "private", "public" e "protected".
Private
Private significa que esse membro é apenas acessível na classe. Então significa que se tentarmos acessar um elemento da classe, receberemos o erro de que esse membro da classe "nome" é inacessível ou que ele não é visível. Isso acontece justamente por termos utilizado essa palavra-chave "private".
private:
std::string nome;
int idade;
int altura_em_cm;
float peso_em_kg;
}
private String nome;
private int idade;
private int altura_em_cm;
private double peso_em_kg;
}
Mas e se quisermos modificar fora da classe? Para isso usamos a palavra "public".
Public
Public significa que esse membro pode ser acessado/modificado fora dele. Em outras palavras, se quisermos mudar o nome da nossa pessoa fora da classe, agora podemos.
class Pessoa {
public: // Especificamos que essas variáveis agora sao publicas
std::string nome;
int idade;
int altura_em_cm;
float peso_em_kg;
};
class Pessoa {
public String nome;
public int idade;
public int altura_em_cm;
public double peso_em_kg;
}
A palavra-chave protected, deixaremos para explorar ela quando falarmos de herança.
Construtor
Você também deve ter notado, que para criar uma instância de uma classe, precisamos invocar essa função com o nome da classe.
Pessoa joao = Pessoa();
Pessoa joao = new Pessoa();
Isso é uma função especial chamada de "Construtor", e ela é sempre chamada quando criamos um objeto de uma classe. No caso, como não especificamos nenhum construtor, então esse construtor vazio, apenas irá iniciar nossa instância, mas não nossos atributos (nome, idade, altura...).
Para isso então, criaremos o nosso construtor:
#include<iostream>
#include<string>
class Pessoa {
std::string nome;
int idade;
int altura_em_cm;
float peso_em_kg;
public:
Pessoa(std::string nome, int idade, int altura_em_cm, float peso_em_kg) {
this->nome = nome;
this->idade = idade;
this->altura_em_cm = altura_em_cm;
this->peso_em_kg = peso_em_kg;
}
// Em c++ nós também temos classes chamadas de "Desconstrutores" que são chamados quando um objeto
// sai do escopo e está prestes a ser deletado, falaremos disso mais tarde.
};
int main(void) {
std::cout << "Hello world!\n";
Pessoa joao = Pessoa("João", 32, 174, 70.3);
return 0;
}
class Pessoa {
private String nome;
private int idade;
private int altura_em_cm;
private double peso_em_kg;
public Pessoa(String nome, int idade, int altura_em_cm, double peso_em_kg) {
this.nome = nome;
this.idade = idade;
this.altura_em_cm = altura_em_cm;
this.peso_em_kg = peso_em_kg;
}
}
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!\n");
Pessoa joao = new Pessoa("João", 32, 174, 70.3);
}
}
Obs.: Podemos ter mais de um construtor em uma classe, ele só precisa ter argumentos diferentes.
Agora criamos um construtor para a nossa classe, lembrando sempre de respeitar a sintaxe das funções de Construtor, onde a função precisa ter o nome da sua classe (Não se esquecendo de especificar o escopo se necessário).
Uma coisa que você deve ter notado é que utilizamos essa palavra "this". Essa palavra serve para referenciarmos especificamente elementos da nossa classe. Não precisamos usar sempre essa palavra para referenciar eles, apenas quando em um mesmo escopo, temos duas variáveis com o mesmo nome.
std::string nome;
int idade;
int altura_em_cm;
float peso_em_kg;
public:
Pessoa(std::string nome, int idade, int altura_em_cm, float peso_em_kg) {
this->nome = nome;
private String nome;
private int idade;
private int altura_em_cm;
private double peso_em_kg;
public Pessoa(String nome, int idade, int altura_em_cm, double peso_em_kg) {
this.nome = nome; // perceba que "this.nome" se refere ao membro da classe "String nome", enquanto que a variável
// "nome" se refere ao parâmetro passado ao construtor da classe
Funções dentro da nossa classe
Para finalizar criaremos uma classe que imprima os dados da minha variável "João", para isso iremos criar funções dentro das nossas classes que possam fazer essa tarefa de converter dados da nossa classe em String.
Em Java, nós podemos sobrescrever a função mágica "toString()" da nossa classe, para reproduzir esse comportamento. Em C++ iremos apenas criar uma função própria para fazer isso e usar uma classe chamada "std::stringstream" para converter os dados da classe para uma String.
#include<iostream>
#include<string>
#include <sstream>
class Pessoa {
std::string nome;
int idade;
int altura_em_cm;
float peso_em_kg;
public:
Pessoa(std::string nome, int idade, int altura_em_cm, float peso_em_kg) {
this->nome = nome;
this->idade = idade;
this->altura_em_cm = altura_em_cm;
this->peso_em_kg = peso_em_kg;
}
std::string to_string() {
std::stringstream s; // criando a nossa stream
s << "Nome: " << nome << "\nIdade: " << idade << " anos\nAltura (em cm): " << altura_em_cm << "cm\nPeso (em kg): " << peso_em_kg << "kg";
return s.str(); // convertendo a stream para um tipo de dado que podemos usar na hora de imprimir
}
};
int main(void) {
std::cout << "Hello world!\n";
Pessoa joao("João", 32, 174, 70.3);
std::cout << joao.to_string() << "\n";
return 0;
}
class Pessoa {
private String nome;
private int idade;
private int altura_em_cm;
private double peso_em_kg;
public Pessoa(String nome, int idade, int altura_em_cm, double peso_em_kg) {
this.nome = nome;
this.idade = idade;
this.altura_em_cm = altura_em_cm;
this.peso_em_kg = peso_em_kg;
}
@Override //Override indica uma função que está sendo sobrescrita, falaremos disso nas próximas aulas
public String toString() {
return "Nome: " + nome + "\n" +
"Idade: " + idade + " anos\n" +
"Altura (em cm): " + altura_em_cm + "cm\n" +
"Peso (em kg): " + peso_em_kg + "kg";
}
}
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!\n");
Pessoa joao = new Pessoa("João", 32, 174, 70.3);
System.out.println(joao);
}
}
E por enquanto é isso, agora você deve ter uma ideia geral de como uma classe funciona. É importante notar que a maioria dessas ideias passamos por alto, e planejo voltar a elas em futuras aulas, então se tudo pareceu muito confuso, não se preocupe.