Ir ao conteúdo
  • Cadastre-se

C++ Gerar vetor em um main através da classe em arquivo de cabeçalho


Thiago1101

Posts recomendados

Olá. Eu estou começando ainda na Programação orientada a objetos, então ainda tenho muitas duvidas sobre o assunto e me desculpe se eu não souber me expressar, mas tentarei ser o mais claro possível. 

Minha duvida é a seguinte, eu criei uma classe e fiz um constructor nela e esse constructor recebe dois parâmetros, um inteiro e um do tipo float. Esse constructor consegue de fato gerar todos os valores de um vetor. Cheguei ate a fazer o teste no main pedindo pra me mostrar um valor numa posição qualquer do vetor gerado no constructor e deu certo. Mas quando eu tento associar esse vetor gerado na classe a um vetor declarado no main, eu não consigo. Como posso fazer uma variável "recolher" o vetor gerado no constructor?

Link para o comentário
Compartilhar em outros sites

tem muitas formas de se fazer isso

 

adicionado 0 minutos depois

mas saiba que você nunca vai poder retornar um vetor emC/C++

 

adicionado 0 minutos depois

você pode usar memória dinamica

adicionado 1 minuto depois

ou usar std::vector

adicionado 1 minuto depois

Um truque é por o vetor dentro de um struct, pois uma struct sim você pode retornar

adicionado 1 minuto depois

manda seu exemplo

Link para o comentário
Compartilhar em outros sites

 DeclararVetor x{m,dx};

 cout << x.x1[m] << endl;

No arquivo main, eu tinha feito desse jeito só pra verificar se o DeclararVetor realmente estava conseguindo salvar todos os valores do vetor. Mas o que eu queria mesmo era salvar todos os valores calculados em DeclararVetor de x1 para a variável x

adicionado 1 minuto depois
#include "decvar.h"
#include <iostream>

DeclararVetor::DeclararVetor(int m, float dx){

     x1 = (float*)malloc((m + 1) * sizeof(float));

	for (int i = 0; i < m + 1; i++)
	{
		x1[i + 1] = x1[i] + dx;

	}

}

Esse foi o Constructor, salvo em arquivo decvar.cpp no meu projeto

Link para o comentário
Compartilhar em outros sites

#include <iostream>
#include <cstdio>
#include "math.h"

#include "decvar.h"

using namespace std;

int main(){

     float dx, dt, L, p, g, E, b, h;
     int m, n, i;

     dx = 0.1;
     dt = pow(10,-9);

     L = 100*dx;
     p = 500*dt;

     m = round(L/dx);
     n = round(p/dt);

     /*
     Tentando fazer a declaração de x como sendo um vetor
     (A função DeclararVetor gera um vetor, mas não consigo salvá-la em uma variável do main)
     */
     DeclararVetor x{m,dx};

     cout << x.x1[m] << endl;




     system("pause");
     return 0;
}

Esse de cima é o arquivo main.cpp

 

#ifndef DECVAR_H_INCLUDED
#define DECVAR_H_INCLUDED

//Aqui é feito a declaração das variáveis e funções que serão utilizadas (Variáveis declaradas aqui são utilizadas no decvar.cpp)

class DeclararVetor{
public:
     float *x1,deltax;
     int mm;

     DeclararVetor(int m, float dx);

};


#endif // DECVAR_H_INCLUDED

decvar.h

 

#include "decvar.h"
#include <iostream>

DeclararVetor::DeclararVetor(int m, float dx){

     x1 = (float*)malloc((m + 1) * sizeof(float));

	for (int i = 0; i < m + 1; i++)
	{
		x1[i + 1] = x1[i] + dx;

	}

}

 

Link para o comentário
Compartilhar em outros sites

ok... pera ae que vou dar uma estudada hehe

adicionado 5 minutos depois

Isso ta errado DeclararVetor x{m,dx};

Se fosse um vetor seria declarado tal como DeclararVetor x[10]={m,dx};

adicionado 6 minutos depois

acho que você n viu também que DeclararVetor x{m,dx}; você usou { e } quando talvez você queria usar ( e )

adicionado 7 minutos depois

Se você quer usar o construtor use ( e ) DeclararVetor x(m,dx);

Link para o comentário
Compartilhar em outros sites

Em 24/07/2019 às 02:01, Thiago1101 disse:

Como posso fazer uma variável "recolher" o vetor gerado no constructor?

 

Colocando o vetor dentro do objeto

 

Em 24/07/2019 às 12:50, Thiago1101 disse:

Mas o que eu queria mesmo era salvar todos os valores calculados em DeclararVetor de x1 para a variável x

 

Isso você faz simplesmente declarando

DeclararVetor x1 = x;

 

Em 24/07/2019 às 13:00, Thiago1101 disse:

/* Tentando fazer a declaração de x como sendo um vetor (A função DeclararVetor gera um vetor, mas não consigo salvá-la em uma variável do main) */ DeclararVetor x{m,dx};

 

Está errado. DeclararVetor é uma classe. E tem um construtor para criar os objetos MeuVetor, chamadas instâncias. A simples declaração já salva tudo. Você só precisa escrever o construtor direito

 

Vou explicar melhor e deixar um exemplo completo, porque acho que esse é um tema importante. Seu programa tem um pé em C e outro em C++ mas está longe ainda da orientação a objetos, então leva pouca vantagem ao usar C++ e não C.

 

Veja com paciência :D 

 

Este seu problema é um bom exemplo para quem está começando a usar uma linguagem orientada a objeto acompanhar, e talvez tenha te faltado uma introdução mais elaborada justamente a esse conceito de objeto. E o que significa isso para tratar um problema, e criar uma classe que vai ser a abstração do problema usando uma dessas linguagens orientada a objetos.

 

Antes de tudo, note que C não é C++ nem C++ é C. São diferentes animais. Bem diferentes. C++ tem um classe vector em std, std::vector, documentada em http://www.cplusplus.com/reference/vector/vector/ que pode resolver seu problema.

C tem uma função em stdlib.h, malloc(), presente também em C++, que pode resolver seu problema, e que você de fato usou em seu programa. Mas em C++ você pode usar new e delete ao invés de malloc e free do C e talvez ache que faz mais sentido.

 

Continue lendo.


Usando std::vector ou new você não precisa alocar memória diretamente nem controlar isso. Usando malloc() em C++ você precisa cuidar desses "detalhes" todos.
Vou te mostrar uma maneira de criar isso em C++ usando new e delete e espero deixar mais evidente a maneira de tratar isso com a orientação clásssica a objetos. 


Espero que tenha paciência. 


Sobre a questão inicial: "como recolher o vetor gerado no construtor?"


A resposta é curta: você tem dois parâmetros no seu construtor: float dx e int m e seu vetor tem (m+1) posições e em cada uma temos um float dx. Você "recolhe" os valores apenas declarando os caras, por exemplo:

 

DeclararVetor vetor1(10,3,14159);
DeclararVetor vetor2(5,2.0);
DeclararVetor ConstrutorNormal(m,dx);


Só isso. E você teria vetor1 vetor2 e ConstrutorComum criados, usando os valores passados na declaração. Porque funciona? Porque você escreve o construtor para fazer tudo de acordo, e o compilador se encarrega de chamar o construtor certo na hora certa


Sobre esse trecho em seu código:

     /*
     Tentando fazer a declaração de x como sendo um vetor
     (A função DeclararVetor gera um vetor, mas não consigo salvá-la em uma variável do main)
     */
     DeclararVetor x{m,dx};

Não é assim que funciona. Entenda que a função DeclararVetor não gera um vetor própriamente. O que gera uma variável da classe, um DeclararVetor, é a simples declaração:

DeclararVetor x1, x2, x3, x4; 

por exemplo geraria 4 vetores, e se diz que são instâncias da classe DeclararVetor. E já estariam "salvos em main". É como declarar
 

 float dx, dy, dz, dt;    /* cria 4 float */

E dentro desses objetos x1, x2, x3 e x4 estariam os valores de cada  vetor, o tamanho e o delta usado. Esse é o grande valor desse conceito, e se chama encapsulamento na literatura.


Acho que já notou que sendo assim DeclararVetor não foi um nome feliz para sua classe, porque o construtor não declara nada. A declaração é que acaba executando o construtor ---  se você tiver escrito um.

 

Citação

No arquivo main, eu tinha feito desse jeito só pra verificar se o DeclararVetor realmente estava conseguindo salvar todos os valores do vetor. Mas o que eu queria mesmo era salvar todos os valores calculados em DeclararVetor de x1 para a variável x


De novo: o construtor não salva o objeto. A declaração faz isso. Só que em geral precisa de um construtor para criar o objeto corretamente. Criar um int ou um float é algo definido. Esses são chamados "tipos primitivos" por isso. Está claro como construir cada um. Para tipos que você define, em geral vai precisar de um ou mais construtores


Como salvar os valores calculados em x1 para a variável x? Simples:

 

DeclararVetor x = x1;

Essa é a mágica dos objetos. Vou explicar a seguir.

 

Citação

Posso utilizar DeclararVetor x[10]={m,dx} mesmo que o m e dx sejam apenas parâmetros para o meu construtor?


Não, não pode. não é assim que funciona.


Seu construtor original, para a gente continuar:
 

DeclararVetor::DeclararVetor(int m, float dx)
{
    x1 = (float*)malloc((m + 1) * sizeof(float));
    for (int i = 0; i < m + 1; i++)
    {
        x1[i + 1] = x1[i] + dx;
    }
}


Não temos a descrição do que você está tentando implementar em main() mas vemos aqui que o seu vetor é uma sequência de (m+1) valores do tipo float e que são alocados dinâmicamente. E são da forma

 a(n+1) = a(n) + b

, uma função simples nesse caso. Então criar um vetor desses significa alocar memória para o total de valores float necessários e calcular os caras. 


Encapsulamento


Esse nome chique significa que ao declarar um trem desses, uma variável dessa classe, ela vai conter esse material todo e eventualmente proteger isso. Um objeto deses vai conter um array de float com os valores calculados, assim que eu declarar.  E o tamanho. E o delta usado para calcular os caras.


Vamos mudar o nome da classe para MeuVetor e você vai ver que faz mais sentido que DeclaraMeuVetor por exemplo. Podia ser assim

 

class MeuVetor
{
    int quantos;

    float delta;
    float * valor = NULL;
}


Só isso? sim. Cada MeuVetor é um objeto, encapsulando um vetor de quantos valores calculados usando o delta dx fornecido. E se declara assim


MeuVetor a,b,c,d;


Agora a gente precisa dar sentido ao objeto. Se fosse em C seria o caso de alocar memória para ele e criar as funções de acesso e seguir adiante. Em C++ podemos criar os construtores, que serão chamados automaticamente cada vez que um objeto desses for criado. Não é preciso controlar nada. O que eu preciso para construir um objeto desses? O tamanho e o delta. Mas eu posso ter vários construtores, outra gentileza da orientação a objetos do C++


Sobrecarga (overloading)


Esse outro nome chique quer dizer que eu posso ter construtores ou funções em C++ que se viram para tratar comandos assim:
MeuVetor v1(10, 3.1415);
MeuVetor v2(100);
MeuVetor v3(20.19);
MeuVetor v4;


E
v1 vai ser o normal como no seu exemplo: 10 valores com dx = 3.1415
v2 vai criar 100 caras com um dx padrão que estabeleceremos como 1.0
v3 vai criar um vetor com tamanho padrão que estabeleceremos como de 10 valores.
v4 vai criar um unico valor igual a zero.


Isso dá uma ideia do poder desse conceito.


Como funciona? Simples: pelo número de parâmetros o compilador determina qual versão do construtor deve usar e a vida segue. Segue depois de você escrever todos os construtores necessários, claro :) Só vou escrever um proque não acrescenta nada: são apenas funções com o mesmo nome mas parâmetros diferentes.


Se você só escrever um, o completo, ok:

 Se declarar de algum outro modo simplesmente dá erro de compilação e pronto.
Eis um possível construtor para essa classe usando os dois parâmetros

    MeuVetor(int n, float dx)
    {
        quantos = n;
        delta = dx;
        vetor = new float[n];
        vetor[0] = 0;
        for (int i = 0; i < (quantos-1); i++)
        {
            vetor[i + 1] = vetor[i] + delta;
        }    // end for
    }    // end constructor


Diferenças em relação ao seu construtor original: alocamos exatamente m valores. E assumimos que o primeiro vale 0. No seu cógido alocava um float a mais e o valor de x1[0] não está explícito. E temos mais uma variável, delta.
Note que você pode declarar o construtor dentro da classe. Pode ficar mais fácil de ler

.
E quando liberar a memória alocada? Note que ao fim do programa o sistema pode cuidar disso, mas e se você declara esses valores dentro de uma função que chama toda hora? Essas instâncias do vetor vão ficando inacessíveis, e assim como em C temos que liberar essa memória. Mas como seria isso em C++?


Destruindo uma instância da classe: destrutores em C++


Então... Isso já foi resolvido: quando preciso você pode escrever um destrutor de classe, e o compilador se vira para rodar isso quando um objeto se torna inacessível, por exemplo quando seu programa termina. Como funciona? Você declara como o construtor, mas com um til na frente. E sem parâmetros. Algo assim por exemplo 

   ~MeuVetor()
    {
          delete[] vetor;    /* eis o que importa: liberar a a memoria alocada quanto da construcao */
    }    // end destructor

 

E a vida segue.


E como copiar um MeuVetor para outro, para eu poder escrever algo como

 

MeuVetor v2 = v1;

Seria legal se eu não precisasse fazer nada... E é quase isso.


Copiando uma classe mais complexa: o construtor de cópia.


Um construtor de cópia, se declarado, é chamado automaticamente quando o compilador vê uma tentativa de copiar um objeto, e tem uma declaração simples assim:

MeuVetor( MeuVetor &original)
{
// codigo necessario para copiar
}

Eis um construtor que copia um vetor direitinho:

    MeuVetor(const MeuVetor& original)
    {
        quantos = original.quantos;
        delta = original.delta;
        vetor = new float[quantos];    
        /* criado. Agora copia os valores */
        for (int i = 0; i < quantos; i++)
        {
            vetor[i] = original.vetor[i];
        }    // end for
    }    // end copy constructor

 

Com o construtor, o destrutor e o construtor de cópia podemos escrever tudo do jeito simples, como se fossem variáveis comuns, tipo int ou float. E o compilador faz tudo rodar na hora certa.
Mas MeuVetor é uma classe e pode aprender coisas. Acho que agora já deve ter entendido como isso é poderoso. Podemos implementar mais coisas em nossa classe sem mexer em nada do resto do programa. E enquanto não mexer nos parâmetros podemos recriar nossos construtores e destrutores a qualquer momento, sem mexer nos programas que usam a classe


Que tal uma função Mostra() que iria mostrar na tela algo assim para o vetor com 4 valores e dx = 3.14159?

----------
Vetor com 4 valores. Delta = 3.14159
Pos: 0 Valor: 0
Pos: 1 Valor: 3.14159
Pos: 2 Valor: 6.28318
Pos: 3 Valor: 9.42477

Assim eu posso escrever

MeuVetor v1(10,3.14159);
v1.Mostra();

E já ver na tela o conteúdo. Isso é encapsulamento. Mostra() é um método da classe. Publico, de modo que você pode usar em qualquer lugar. Você pode ter métodos private que só podem ser usados dentro da classe, e muitas vezes é exatamente o que você quer. Isso também se aplica às variáveis: quantos e delta podem ser declarados como private e ninguém pode então mudar esses valores de fora da classe. E pensando bem isso seria bom porque ia dar uma confusão alguem escrever

v1.quantos = 300

sem alocar a memória de novo e tal...

 

Uma implementação de Mostra()


	/* claro que vamos querer ver o que tem dentro */
	void Mostra()
	{
		cout <<
			endl << "----------\n" <<
			"Vetor com " << quantos << " valores. Delta = " <<
			delta << endl;
		for (int i = 0; i < quantos; i++)
		{
			cout << 
				"Pos: " << i <<
				" Valor: " << vetor[i] <<
				endl;
		}	// end for
	}
};	// end Mostra()


Testando isso em main()

 

Um teste óbvio seria declarar uns vetores, copiar, mostra e encerrar, certo? E com umas mensagens para acompanhar.


Exemplo de teste em main()

    cout << "Criando v1 com 4 valores e delta = 3.14159" << endl;
    MeuVetor v1(4, 3.14159);
    cout << "Criando v2 com 3 valores e delta = 2.0" << endl;
    MeuVetor v2(3, 2.0);
    cout << "copiando v1 em v3" << endl;
    MeuVetor v3 = v1;
    cout << "copiando v2 em v4" << endl;
    MeuVetor v4 = v2;

    cout << endl << "Mostrando v1" << endl;
    v1.Mostra();
    cout << endl << "Mostrando v2" << endl;
    v2.Mostra();
    cout << endl << "Mostrando v3 = v1" << endl;
    v3.Mostra();
    cout << endl << "Mostrando v2 = v4" << endl;
    v4.Mostra();

Isso deveria gerar algo assim
 

Criando v1 com 4 valores e delta = 3.14159
    +++++ Construtor: construindo vetor com 4 valores e delta = 3.14159
Criando v2 com 3 valores e delta = 2.0
    +++++ Construtor: construindo vetor com 3 valores e delta = 2
copiando v1 em v3
    copia Construindo uma copia: copiando vetor com 4 valores e delta = 3.14159
copiando v2 em v4
    copia Construindo uma copia: copiando vetor com 3 valores e delta = 2

Mostrando v1

----------
Vetor com 4 valores. Delta = 3.14159
Pos: 0 Valor: 0
Pos: 1 Valor: 3.14159
Pos: 2 Valor: 6.28318
Pos: 3 Valor: 9.42477

Mostrando v2

----------
Vetor com 3 valores. Delta = 2
Pos: 0 Valor: 0
Pos: 1 Valor: 2
Pos: 2 Valor: 4

Mostrando v3 = v1

----------
Vetor com 4 valores. Delta = 3.14159
Pos: 0 Valor: 0
Pos: 1 Valor: 3.14159
Pos: 2 Valor: 6.28318
Pos: 3 Valor: 9.42477

Mostrando v2 = v4

----------
Vetor com 3 valores. Delta = 2
Pos: 0 Valor: 0
Pos: 1 Valor: 2
Pos: 2 Valor: 4

C:\Users\toninho\source\repos\OVetor\Debug\OVetor.exe (process 6812) exited with code 0.
Press any key to close this window . . .

Só tenho esse compilador. Não sei o que você usa. Mas essa é a saída do MicroSoft Visual Studio Community 2019, grátis. 


Eis o código da classe, com o programa de teste também. Acho que vai entender as diferenças. Coloquei vários cout, equivalentes aos printf do C para você ver na tela o que está acontecendo


Em caso de não entender... escreva de novo

 

O programa completo

 

#include <iostream>
#include <stdlib.h>

using namespace std;

class MeuVetor
{
public:

	int quantos;
	float delta;
	float * vetor = NULL;	/* na construcao do objeto vai ser alocado o tamanho correto */

	/* construtor com dois parametros */
	MeuVetor(int n, float dx)
	{
		cout << "    +++++ Construtor: construindo vetor com " << n << " valores e delta = " << dx << endl;
		quantos = n;
		delta = dx;
		vetor = new float[n];
		vetor[0] = 0;
		for (int i = 0; i < (quantos-1); i++)
		{
			vetor[i + 1] = vetor[i] + delta;
		}	// end for
	}	// end constructor

	/* destrutor nao tem parametros */
	~MeuVetor()
	{
		cout << "    ----- Destrutor: fim da vida para um vetor com " << quantos << " valores e delta = " << delta << endl;
		delete[] vetor;	/* eis o que importa: liberar a a memoria alocada quanto da construcao */
	}	// end destructor

	/* construtor de copia: essencial porque precisa alocar memoria e copiar tudo */
	MeuVetor(const MeuVetor& original)
	{
		cout << 
			"    copia Construindo uma copia: copiando vetor com " <<
		    original.quantos << 
			" valores e delta = " << 
			original.delta << 
			endl;

		quantos = original.quantos;
		delta = original.delta;
		vetor = new float[quantos];	
		/* criado. Agora copia os valores */
		for (int i = 0; i < quantos; i++)
		{
			vetor[i] = original.vetor[i];
		}	// end for
	}	// end copy constructor

	/* claro que vamos querer ver o que tem dentro */
	void Mostra()
	{
		cout <<
			endl << "----------\n" <<
			"Vetor com " << quantos << " valores. Delta = " <<
			delta << endl;
		for (int i = 0; i < quantos; i++)
		{
			cout << 
				"Pos: " << i <<
				" Valor: " << vetor[i] <<
				endl;
		}	// end for
	}
};	// end Mostra()

int main()
{
	cout << "Criando v1 com 4 valores e delta = 3.14159" << endl;
	MeuVetor v1(4, 3.14159);
	cout << "Criando v2 com 3 valores e delta = 2.0" << endl;
	MeuVetor v2(3, 2.0);
	cout << "copiando v1 em v3" << endl;
	MeuVetor v3 = v1;
	cout << "copiando v2 em v4" << endl;
	MeuVetor v4 = v2;

	cout << endl << "Mostrando v1" << endl;
	v1.Mostra();
	cout << endl << "Mostrando v2" << endl;
	v2.Mostra();
	cout << endl << "Mostrando v3 = v1" << endl;
	v3.Mostra();
	cout << endl << "Mostrando v2 = v4" << endl;
	v4.Mostra();
	exit(0);	// fim
}

:D É isso. 

  • Curtir 1
  • Obrigado 2
Link para o comentário
Compartilhar em outros sites

2 minutos atrás, Thiago1101 disse:

@arfneto , agora consegui fazer o que queria. Meu programa tá compilando do jeito que eu precisava pra poder continuar meu projeto. Obrigado pela ajuda!

 

Muito bem!
Esqueci de citar mas acho que já sabe agora: dentro de cada instância do MeuVetor você tem os valores, de modo que se que se você tem lá 

MeuVetor x(10, 3.0);
MeuVetor y(20, 0.5);

Pode acessar o conteúdo assim:

float valor = x.vetor[2];
y.vetor[0]=0;
int quantosX = x.quantos;
float deltaY = y.delta;
y.Mostra();

Coisas assim.

Espero que tenha entendido o caso do construtor de cópia e como e porque ele funciona. E a necessidade do destrutor para liberar a memória. É essencial que entenda essas coisas para programar com objetos.

  • Obrigado 1
Link para o comentário
Compartilhar em outros sites

Esqueci de comentar isso: 

 

Citação

"Gerar vetor em um main através da classe em arquivo de cabeçalho"

 

O título do tópico, sim...

 

Não faz sentido nem tem a ver com seu problema tampouco. Um arquivo de cabeçalho não é algo assim ativo, é apenas uma maneira de antecipar as declarações para o compilador, já que em muitos casos o código de classes está em arquivos separados e podem estar já compilados, então você precisa de um arquivo contendo apenas as declarações para orientar a compilação.

 

E como eu disse, gerar um vetor em main---  ou em qualquer outra função no programa --- é apenas uma questão de declarar o que se chama uma instância da variável. Isso faz o compilador localizar o construtor correto se declarado, ou um construtor padrão se nenhum construtor for encontrado, e executá-lo.

 

É convencional gerar as classes públicas em arquivos separados, e criar um arquivo de cabeçalhos com as declarações e incluir nos lugares apropriados usando #include

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Voltando ao essencial e aos objetos

 

Imaginei que você ia voltar com um post depois de um crash de seu projeto, mas talvez tenha evoluído no projeto ou tenha se limitado ao contexto do exemplo aqui, já que resolveu seu problema imediato :) 

 

De qualquer forma vou mostrar aqui um pouco do que acontece depois em geral, quando você tem uma classe enjoada como essa dos vetores, que aloca os vetores na hora. Isso porque é um caso genérico bacana como exemplo e pode ser útil pra muitos, por alertas para os riscos ao escrever esse tipo de código em C ou C++: você precisa se preocupar com a memória alocada.

 

Esse trecho  de programa

int teste_copy_constructor()
{
    cout << "\n-------------------- teste copy constructor --------------------" << endl;

    cout << "Criando v1 com 4 valores e delta = 3.14159" << endl;
    MeuVetor v1(4, (float)3.14159);
    cout << "Criando v2 com 3 valores e delta = 2.0" << endl;
    MeuVetor v2(3, 2.0);
    cout << "copiando v1 em v3" << endl;
    MeuVetor v3 = v1;
    cout << "copiando v2 em v4" << endl;
    MeuVetor v4 = v2;

    cout << endl << "Mostrando v1" << endl;
    v1.Mostra();
    cout << endl << "Mostrando v2" << endl;
    v2.Mostra();
    cout << endl << "Mostrando v3 = v1" << endl;
    v3.Mostra();
    cout << endl << "Mostrando v2 = v4" << endl;
    v4.Mostra();
    cout << "-------------------- teste copy constructor --------------------\n" << endl;

    return 0;
}

É o código do exemplo que deixei aqui antes e você chama assim em main() por exemplo:

int main()
{
    teste_copy_constructor();
    cout << "\nDe volta a main()\n" << endl;

E gera isso na tela

-------------------- teste copy constructor --------------------
Criando v1 com 4 valores e delta = 3.14159
    +++++ Construtor: construindo vetor com 4 valores e delta = 3.14159
Criando v2 com 3 valores e delta = 2.0
    +++++ Construtor: construindo vetor com 3 valores e delta = 2
copiando v1 em v3
    +++++ Construtor de copia: vetor com 4 valores e delta = 3.14159
copiando v2 em v4
    +++++ Construtor de copia: vetor com 3 valores e delta = 2

Mostrando v1

----------
Vetor com 4 valores. Delta = 3.14159
Pos: 0 Valor: 0
Pos: 1 Valor: 3.14159
Pos: 2 Valor: 6.28318
Pos: 3 Valor: 9.42477

Mostrando v2

----------
Vetor com 3 valores. Delta = 2
Pos: 0 Valor: 0
Pos: 1 Valor: 2
Pos: 2 Valor: 4

Mostrando v3 = v1

----------
Vetor com 4 valores. Delta = 3.14159
Pos: 0 Valor: 0
Pos: 1 Valor: 3.14159
Pos: 2 Valor: 6.28318
Pos: 3 Valor: 9.42477

Mostrando v2 = v4

----------
Vetor com 3 valores. Delta = 2
Pos: 0 Valor: 0
Pos: 1 Valor: 2
Pos: 2 Valor: 4
-------------------- teste copy constructor --------------------

    ----- Destrutor: fim da vida para um vetor com 3 valores e delta = 2
    ----- Destrutor: fim da vida para um vetor com 4 valores e delta = 3.14159
    ----- Destrutor: fim da vida para um vetor com 3 valores e delta = 2
    ----- Destrutor: fim da vida para um vetor com 4 valores e delta = 3.14159

De volta a main()

E funciona direitinho: cria os quatro vetores: v4 é copia de v2, v3 é cópia de v1, e as cópias alocam memória para o vetor quando preciso e o construtor também. E quando volta do teste para main os vetores perdem o foco e são destruídos, com a memória liberada.

 

Tudo certo então? Não mesmo. Se você escrever esse

int teste_atribuicao()
{
    cout << "\n-------------------- teste atribuicao vetor a = vetor b --------------------" << endl;
    cout << "criou v1 e v2" << endl;
    MeuVetor v1(2, (float)3.14159);
    MeuVetor v2(4, (float)2.0);
    v1.Mostra();
    v2.Mostra();
    v2 = v1;
    v2.Mostra();
    cout << "copiou. Tera funcionado?" << endl;
    cout << "-------------------- teste atribuicao vetor a = vetor b --------------------\n" << endl;
    return 0;
}    // end teste_atribuicao

E rodar assim:

    teste_atribuicao();
    cout << "\nDe volta a main()\n" << endl;

Vai ver que seu programa vai pro espaço. Porque? Veja o que sai na tela:

-------------------- teste atribuicao vetor a = vetor b --------------------
criou v1 e v2
    +++++ Construtor: construindo vetor com 2 valores e delta = 3.14159
    +++++ Construtor: construindo vetor com 4 valores e delta = 2

----------
Vetor com 2 valores. Delta = 3.14159
Pos: 0 Valor: 0
Pos: 1 Valor: 3.14159

----------
Vetor com 4 valores. Delta = 2
Pos: 0 Valor: 0
Pos: 1 Valor: 2
Pos: 2 Valor: 4
Pos: 3 Valor: 6

----------
Vetor com 2 valores. Delta = 3.14159
Pos: 0 Valor: 0
Pos: 1 Valor: 3.14159
copiou. Tera funcionado?
-------------------- teste atribuicao vetor a = vetor b --------------------

    ----- Destrutor: fim da vida para um vetor com 2 valores e delta = 3.14159
    ----- Destrutor: fim da vida para um vetor com 2 valores e delta = 3.14159

Parece tudo bem... Criou os dois objetos, depois copiou o primeiro no segundo, imprimiu os valores, e retornou. Mas então...

erro.png.41060c8d84f05f79bc52660d457224f8.png

 

Seu programa aborta e o que vai aparecer depende claro de seu ambiente de execução...

 

A vida é injusta. Porque isso agora? 

 

Qualquer operação com variáveis desse tipo, ditas instâncias dessa classe MeuVetor, tem que ser programada com cuidado. Eis o que acontece: quando você declara

MeuVetor v1 = v2;
MeuVetor v3 = v4;

é muito diferente de escrever depois

v3 = v1;

 

Na declaração, como você escreveu o construtor de cópia o compilador percebe a parada e chama o construtor correto. Veja as mensagens na tela. E então aloca memória para o vetor novo e copia os dados um a um por conta. E quando vai destruir o objeto o código do destrutor ~MeuVetor() apaga tudo certinho.

 

Mas quando você escreve v3=v1 o compilador vê uma atribuição e na falta de mais informação simplesmente copia todos os valores. Se você chama v3.Mostra() até pode imprimir tudo direitinho, só que o vetor também é copiado, o endereço do vetor.

 

Veja como é a classe:

class MeuVetor
{
public:
    int quantos;
    float delta;
    float * vetor = NULL;    /* na construcao do objeto vai ser alocado o tamanho correto */

...

Então quando v1 é destruído v3 aponta para uma região de memória que já foi liberada. E quando você tentar acessar os dados em v3.vetor seu programa já era. E se ele não cancelar pode ser pior porque a área onde estava v3.vetor pode já ter outros dados e vai ser o diabo encontrar um erro desses.

 

Você precisa definir o processo de atribuição para quando aparecer uma linha tipo 

// para v1, v2 e v3 da classe MeuVetor

v3 = v1;		/* normal */
v3 = v2 = v1; /* perfeitamente legal */

o código trate de criar os vetores certinho, como no construtor de cópia.

 

É preciso redefinir o operador de atribuição e outros operadores de que pode precisar usar, como ==

 

Assim quando aparecer uma expressão dessas o compilador gera o código correto e o programa continua.

 

Um exemplo para o operador =

 

	const MeuVetor& operator= (const MeuVetor& original)
	{
		cout << "    ===== Atribuicao x = x1: vetor com " << 
			original.quantos << " valores e delta = " << original.delta << endl;
		if (vetor != NULL)
		{	// ja tinha um vetor aqui então libera antes de alocar o novo
			cout << "          vetor com " << quantos <<
				" elementos sera substituido por vetor com " << original.quantos << 
				" elementos " << endl;
			delete[] vetor;
		}
		// agora copia mesmo
		quantos = original.quantos;
		delta = original.delta;
		vetor = new float[quantos];
		/* criado. Agora copia os valores */
		for (int i = 0; i < quantos; i++)
		{
			vetor[i] = original.vetor[i];
		}	// end for
		return *this;
	}	// end operator+()

 

Nada especial mas é bem poderoso você poder fazer isso. Redefinindo os operadores você pode usar soma, divisão, incremento, qualquer coisa com suas classes e rodar o código como você precisa. Imagine uma classe trem composta de classes vagao. Você pode somar um vagao ao trem, mas precisa somar o peso do vagao ao trem, alocar o numero do vagao no descritivo da composição, aumentar o peso e a capacidade de carga, sei lá. Muitos processos podem estar incluídos num simples comando v = v1 + v2 + v3;  por exemplo. E TODO o código contido seguramente no método que implementa o operador, para cada operador.

 

Note que quando você escreve

v3 = v1;

no nosso caso, v3 provavelmente já contem um vetor de dados e a gente precisa liberar essa memória antes de prosseguir.

 

Uma outro comodidade comum para programas de teste: redefinir <<

 

 

Escrevemos uma função Mostra() e usamos assim:

    cout << endl << "Mostrando v1" << endl;
    v1.Mostra();

E vemos na tela 
 

Mostrando v(5,1.5)

----------
Vetor com 5 valores. Delta = 1.5
Pos: 0 Valor: 0
Pos: 1 Valor: 1.5
Pos: 2 Valor: 3
Pos: 3 Valor: 4.5
Pos: 4 Valor: 6

Um pouco chato.  Não seria perfeito poder usar 

cout << v1 << endl;

e já imprimir como a gente quer? Para esses milhões de programas de teste que se escreve nesses cursos de introdução vale a pena porque agiliza os testes, não?

 

 E como seria isso? Pensando bem, cout é declarada como ostream em std. Então se eu escrever algo assim na classe MeuVetor redefinindo o operador << para quando eu tiver algo assim ostream << MeuVetor o compilador vai fazer como fez no caso do operador = e chamar a rotina certa, e não precisamos mais da função Mostra(). E podemos escrever todo tipo de teste lá naquela função. Seria algo assim a declaração:

 

ostream& operator << ( ostream& destino, const MeuVetor& v)

E aí quando aparecer algo como cout << MeuVetorPreferido o compilador vai ver que std::cout é uma ostream e MeuVetorPreferido é da classe MeuVetor e vai chamar nosso método esperto que  podia ser assim escrito

	//
	// redefinindo o operador << para essa classe ai podemos
	// usar cout << vetor que funciona: nada mal
	//
	friend ostream& operator << ( ostream& destino, const MeuVetor& v)
	{
		destino <<
			endl << 
			"----------\n" <<
			"Vetor com " << v.quantos << " valores. Delta = " << v.delta << 
			endl;
		for (int i = 0; i < v.quantos; i++)
		{
			destino <<
				"Pos: " << i <<
				" Valor: " << v.vetor[i] <<
				endl;
		}	// end for
		return destino;
	}	// end operator<<

E chamar assim:

    MeuVetor v(5, 1.5);
    cout << endl << "Mostrando v(5,1.5)" << endl;
    v.Mostra();
    cout << endl << "(*) Mostrando v(5,1.5)" << v << endl;

E vai imprimir


    +++++ Construtor: construindo vetor com 5 valores e delta = 1.5

Mostrando v(5,1.5)

----------
Vetor com 5 valores. Delta = 1.5
Pos: 0 Valor: 0
Pos: 1 Valor: 1.5
Pos: 2 Valor: 3
Pos: 3 Valor: 4.5
Pos: 4 Valor: 6

(*) Mostrando v(5,1.5)
----------
Vetor com 5 valores. Delta = 1.5
Pos: 0 Valor: 0
Pos: 1 Valor: 1.5
Pos: 2 Valor: 3
Pos: 3 Valor: 4.5
Pos: 4 Valor: 6

Então não precisa usar mais v.Mostra() e podemos escrever direto cout << v
 

Se alguém teve paciência de ler até aqui uma última nota: essa sintaxe pode parecer mágica mas é muito poderosa. Num segundo momento ao ler a declaração ela de fato faz sentido. E pode gerar programas muito poderosos e simples de ler, porque a = a - b por exemplo pode funcionar para classes muito complexas e encapsular num simples ato de subtrair uma variável de outra um processo administrativo complexo por exemplo.

 

Vamos programar

 

O programa de teste pode ser copiado aqui: https://github.com/ARFNetoOnCPP/VetorNoForum

 

C++ é uma linguagem enorme e gera programas muito rápidos. Mas com o poder vem a responsabilidade :D

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...