#include <iostream>

using std::cout;
using std::endl;
using namespace std;

class Objeto{
    public:
        int valor;

        Objeto(){
            this->valor = 2;
        }
};

void prop1(Objeto arg) {
	arg.valor = 5; // Não afeta o objeto original
}

void redef1(Objeto arg) {
	Objeto novo;
	novo.valor = 10;
	arg = novo; // Não afeta o objeto original
}

void prop2(Objeto* arg) {
	arg->valor = 5; // Afeta o objeto original
}

void redef2(Objeto* arg) {
	Objeto* novo = new Objeto();
	novo->valor = 10;
	arg = novo; // Não afeta o objeto original
}

void prop3(Objeto& arg) {
	arg.valor = 50; // Afeta o objeto original
}

void redef3(Objeto& arg) {
	Objeto novo;
	novo.valor = 100;
	arg = novo; // Afeta o objeto original
}

void prop4(Objeto& arg) {
	arg.valor = 50; // Afeta o objeto original
}

void redef4(Objeto& arg) {
	Objeto* novo = new Objeto();
	novo->valor = 100;
	arg = *novo; // Afeta o objeto original
}

int main() {
    Objeto valor; // Um objeto no stack
    Objeto* ponteiro = &valor; // Um ponteiro para um objeto no stack ou no heap
    Objeto& referencia = valor; // Uma referência para um objeto no stack ou no heap
    
    cout << "Tipo valor" << endl;
    cout << "Valor inicial: " << valor.valor << endl;
    prop1(valor);
    cout << "Após mudar propriedade: " << valor.valor << endl;
    redef1(valor);
    cout << "Após redefinir: " << valor.valor << endl;
    cout << endl;
    
    cout << "Tipo ponteiro" << endl;
    cout << "Valor inicial: " << (*ponteiro).valor << endl;
    prop2(ponteiro);
    cout << "Após mudar propriedade: " << ponteiro->valor << endl;
    redef2(ponteiro);
    cout << "Após redefinir: " << ponteiro->valor << endl;
    cout << endl;

    cout << "Tipo referência" << endl;
    cout << "Valor inicial: " << referencia.valor << endl;
    prop3(referencia);
    cout << "Após mudar propriedade: " << referencia.valor << endl;
    redef3(referencia);
    cout << "Após redefinir: " << referencia.valor << endl;
    cout << endl;
    
    // Alternativa
    cout << "Tipo referência (alternativa)" << endl;
    Objeto& referencia2 = *(new Objeto()); // Uma referência para um objeto no heap
    cout << "Valor inicial: " << referencia2.valor << endl;
    prop4(referencia2);
    cout << "Após mudar propriedade: " << referencia2.valor << endl;
    redef4(referencia2);
    cout << "Após redefinir: " << referencia2.valor << endl;
    cout << endl;
}

