Arrays multidimensionais, escopo de variável, classes e um problemão [RESOLVIDO]

1. Arrays multidimensionais, escopo de variável, classes e um problemão [RESOLVIDO]

Silas Henrique
silash35

(usa Arch Linux)

Enviado em 04/06/2018 - 14:15h

Esse problema é mais fácil mostrar do que explicar, oque eu quero é criar um array multidimensional em uma classe, o problema é que o tamanho desse array vai ser determinado pelo construtor. Todas as tentativas que eu já fiz não dão certo.

class exemplo{
public:
int x,y;
int array[x][y]; //se eu declarar o array aqui, não funciona porque o valor de x e y ainda não foram determinados pelo construtor;

exemplo(int, tempx, int tempy){
x = tempx;
y = tempy;
int array[x][y]; //aqui ele cria o array, porem ele fica limitado ao escopo do construtor e eu preciso usar em outros métodos da classe
}

int array[x][y] // se eu declarar aqui também da erro

void showarray(){
//codigo que vai dar um 'cout' no array
}
};

int main(){

exemplo a1(10,5);
a1.array[3][4] = 40;
a1.showarray();
}



  


2. MELHOR RESPOSTA

Fernando
phoemur

(usa Debian)

Enviado em 04/06/2018 - 21:22h

Usando alocação dinâmica:

Estilo C++98 com RAII

#include <bits/stdc++.h>

using namespace std;

class exemplo {
int x, y;
int **array;
public:
exemplo(int i, int j)
: x(i), y(j)
{
array = new int* [x];
for (int i=0; i<y; ++i) {
array[i] = new int[y];
std::memset(array[i], 0, sizeof(array[i][y]) * y);
}
}

~exemplo()
{
for (int i=0; i<y; ++i) {delete[] array[i];}

delete[] array;
}

void print()
{
for (int i=0; i<x; ++i) {
for (int j=0; j<y; ++j) {
cout << array[i][j] << " ";
}
cout << endl;
}
}
};

int main()
{
exemplo E (10, 10);
E.print();

return 0;
}


Estilo STL com vector

#include <bits/stdc++.h>

using namespace std;

class exemplo {
int x, y;
vector<vector<int>> array;
public:
exemplo(int i, int j)
: x{i}, y{j}, array(x, vector<int>(y, 0)) {}

void print()
{
for (int i=0; i<x; ++i) {
for (int j=0; j<y; ++j) {
cout << array[i][j] << " ";
}
cout << endl;
}
}
};

int main()
{
exemplo E {10, 10};
E.print();

return 0;
}



Usando templates.
Porém note que a classe exemplo terá dois parâmetros int resolvidos em tempo de compilação de forma que, assim como o Paulo falou, a cada tamanho diferente que você passar você vai ter um tipo diferente, pois os parametros passados por templates fazem parte do tipo.
Da mesma forma que um vector<int> é diferente de um vector<double>, seu exemplo<10, 10> será de tipo diferente de um exemplo<100,100>

#include <bits/stdc++.h>

using namespace std;

template<int X, int Y>
class exemplo {
int array[X][Y];
public:
constexpr exemplo()
{
std::memset(array, 0, sizeof(array[0][0]) * X * Y);
}

void print()
{
for (int i=0; i<X; ++i) {
for (int j=0; j<Y; ++j) {
cout << array[i][j] << " ";
}
cout << endl;
}
}
};

int main()
{
exemplo<10,10> E {};
E.print();

return 0;
}


3. Re: Arrays multidimensionais, escopo de variável, classes e um problemão

Paulo
paulo1205

(usa Ubuntu)

Enviado em 04/06/2018 - 17:41h

O tamanho do objeto tem de ser determinado em tempo de compilação. Assim sendo, arrays tradicionais exigem que você tenha dimensões fixas.

No seu programa, você tem tamanhos constantes nas declarações que aparecem em main(). Sendo fixos, você tem mais opções a usar.

Algumas alternativas são:

  • Usar containers (provavelmente std::vector da biblioteca padrão, ou algum equivalente do Qt, Boost ou do seu toolkit favorito) para criar arrays dinâmicos. Entre suas vantagens estão ter um único tipo capaz de representar matrizes de quaisquer tamanhos e permitir que as matrizes sejam redimensionadas. Por outro lado, o diagnóstico de operações irregulares (por exemplo: tentativas de operar com matrizes com tamanhos incompatíveis com a operação solicitada) só tem como acontecer em tempo de execução.
template <class T=double>
class matriz {
vector<vector<T>> _mat;

public:
matriz(size_t lin, size_t col){
if(lin==0 || col==0)
throw invalid_argument("tentativa de criar matriz com dimensão nula");
_mat.resize(lin, vector<T>(col));
}
matriz(size_t n): matriz(n, n) { } // Constroi matriz quadrada reaproveitando construtor retangular.

size_t linhas() const { return _mat.size(); }
size_t colunas() const { return _mat[0].size(); }

// Acesso a elementos usando parênteses (e.g. “mat(lin, col)”).
T &operator()(size_t l, size_t c){ return _mat.at(l).at(c); } // Permite modificar valor (e.g. “m(1,2)=5.”).
T operator()(size_t l, size_t c) const { return _mat.at(l).at(c); } // Para consultar valor de uma matriz constante.

T determinante() const {
if(linhas()!=colunas())
throw logic_error("tentativa de calcular determinante de matriz não-quadrada");
T det;
/* Calcula o determinante e o armazena de ‘det’. */
return det;
}

matriz inversa(){
if(linhas()!=colunas())
throw logic_error("tentativa de inverter matriz não-quadrada");
if(determinante()==0)
throw runtime_error("tentativa de inverter matriz singular");
matriz inv(linhas());
/* Calcula a inversa da matriz, cujos elementos são postos em inv. */
return inv;
}

/* Outras funções-membro. */
};

// Multiplicação de matrizes.
template <class T, class U> matriz<decltype(T(0)*U(0))> operator*(const matriz<T> &m1, const matriz<U> &m2){
if(m1.colunas()!=m2.linhas())
throw invalid_argument("dimensões erradas para multiplicação");
matriz<decltype(T(0)*U(0))> produto(m1.linhas(), m2.colunas());
for(size_t l1=0; l1<m1.linhas(); l1++)
for(size_t c2=0; c2<m2.colunas(); c2++){
produto(l1, c2)=m1(l1, 0)*m2(0, c2);
for(size_t c1l2=1; c1l2<m1.colunas(); c1l2++)
produto(l1, c2)+=m1(l1, c1l2)*m2(c1l2, c2);
}
return produto;
}

int main(){
matriz<> m1(4, 3), m2(3), m3(3, 4);

/* Popula m1, m2 e m3. */

// Operações válidas.
auto m1_m2=m1*m2; // Colunas de m1 == linhas de m2.
auto det_m2=m2.determinante(); // m2 é quadrada, logo tem determinante.
auto m1_m3=m1*m3; // m1_m3 terá dimensão 4×4.
auto m3_m1=m3*m1; // m3_m1 terá dimensão 3×3.

// Operações inválidas (vão compilar, mas garantidamente darão erro de execução).
matriz<> m0(0); // ERRO: matriz com dimensão nula.
auto m2_m1=m2*m1; // ERRO: colunas de m2 != linhas de m1.
auto det_m1=m1.determinante(); // ERRO: m1 não é quadrada.

// Operações que realmente só podem ser avaliadas em tempo de execução.
auto inv_m2=m2.inversa(); // Pode falhar se o determinante for nulo.
}


  • Usar templates com parâmetros numéricos que indicam as dimensões, criando efetivamente tipos distintos para matrizes com dimensões diferentes. É vantajoso para fazer com que o compilador verifique a adequação de certas operações já em tempo de compilação, evitando que alguns erros lógicos apareçam apenas durante a execução. Por outro lado, todas as matrizes teriam de ter dimensões estáticas, e corre-se o risco de que o tamanho de código gerado a partir de templates ocupe muito espaço, já que cada especialização produz sua própria classe e suas próprias funções. Além disso, embora o compilador possa interceptar erros lógicos, as mensagens mostradas podem não ser muito descritivas ou indicativas de como os corrigir, e ainda haverá casos de erros que só podem ser percebidos em tempo de execução.
template <size_t L, size_t C=L, class T=double>
class matriz {
static_assert(L>0 && C>0, "matriz não pode ter dimensão nula");
T _mat[L][C];

public:
/* Não precisa de construtores para os casos triviais. */

constexpr size_t linhas(){ return L; }
constexpr size_t colunas(){ return C; }

// Acesso a elementos usando parênteses (e.g. “mat(lin, col)”).
T &operator()(size_t l, size_t c){ // Permite modificar valor (e.g. “m(1,2)=5.”).
if(l>=L || c>=C)
throw range_error("índices inválidos");
return _mat[l][c];
}

T operator()(size_t l, size_t c) const { // Para consultar valor de uma matriz constante.
if(l>=L || c>=C)
throw range_error("índices inválidos");
return _mat[l][c];
}

T determinante() const {
static_assert(L==C, "matriz deve ser quadrada para calcular determinante");
T det;
/* Calcula o determinante e o armazena de ‘det’. */
return det;
}

matriz<L, L, T> inversa(){
static_assert(L==C, "matriz deve ser quadrada para poder ser invertida");
if(determinante()==0)
throw runtime_error("tentativa de inverter matriz singular");
matriz<L, L, T> inv;
/* Calcula a inversa da matriz, cujos elementos são postos em inv. */
return inv;
}

/* Outras funções-membro. */
};

// Multiplicação de matrizes.
template <size_t L1, size_t C1L2, size_t C2, class T, class U>
matriz<L1, C2, decltype(T(0)*U(0))> operator*(const matriz<L1, C1L2, T> &m1, const matriz<C1L2, C2, U> &m2){
matriz<L1, C2, decltype(T(0)*U(0))> produto;
for(size_t l1=0; l1<L1; l1++)
for(size_t c2=0; c2<C2; c2++){
produto(l1, c2)=m1(l1, 0)*m2(0, c2);
for(size_t c1l2=1; c1l2<C1L2; c1l2++)
produto(l1, c2)+=m1(l1, c1l2)*m2(c1l2, c2);
}
return produto;
}

int main(){
matriz<4, 3> m1; // 4×3
matriz<3> m2; // 3×3
matriz<3,4> m3; // 3×4

/* Popula m1, m2 e m3. */

// Operações válidas.
auto m1_m2=m1*m2;
auto det_m2=m2.determinante();
auto m1_m3=m1*m3; // m1_m3 terá tipo matriz<4, 4, double>.
auto m3_m1=m3*m1; // m3_m1 terá tipo matriz<3, 3, double>.

// Operações inválidas (vão disparar erros durante a compilação).
matriz<0> m0; // vai violar static_assert da declaração da classe.
auto m2_m1=m2*m1; // não vai encontrar operator*() compatível.
auto det_m1=m1.determinante(); // vai violar static_assert em matriz<4, 3>::determinante().

// Operações que realmente só podem ser avaliadas em tempo de execução.
auto inv_m2=m2.inversa(); // Pode falhar se o determinante for nulo.
}



Entretanto, se você tivesse algo completamente variável, apenas a primeira das alternativas acima seria válida.

int main(){
size_t x, y;
cin >> x >> y;
matriz<> m(x, y); // Versão do primeiro dos meus exemplos. OK.
// Se se tentasse usar o segundo exemplo, com algo como “matriz<x, y>”, o compilador não permitiria,
// pois x e y não são constantes conhecidas em tempo de compilação.
}



4. Acho que tem um erro ai

Silas Henrique
silash35

(usa Arch Linux)

Enviado em 05/06/2018 - 12:27h

phoemur escreveu:

Usando alocação dinâmica:

Estilo C++98 com RAII

#include <bits/stdc++.h>

using namespace std;

class exemplo {
int x, y;
int **array;
public:
exemplo(int i, int j)
: x(i), y(j)
{
array = new int* [x];
for (int i=0; i<y; ++i) {
array[i] = new int[y];
std::memset(array[i], 0, sizeof(array[i][y]) * y);
}
}

~exemplo()
{
for (int i=0; i<y; ++i) {delete[] array[i];}

delete[] array;
}

void print()
{
for (int i=0; i<x; ++i) {
for (int j=0; j<y; ++j) {
cout << array[i][j] << " ";
}
cout << endl;
}
}
};

int main()
{
exemplo E (10, 10);
E.print();

return 0;
}


Acho que seu destrutor não vai desalocar tudo não, acho que como você alocou com um loop, talvez você tenha que desalocar com um loop também
_______________________________
Amar é... deletar o Windows do HD !


5. Re: Arrays multidimensionais, escopo de variável, classes e um problemão [RESOLVIDO]

Fernando
phoemur

(usa Debian)

Enviado em 05/06/2018 - 13:09h

Mas ele está deletando em um loop...
Pode checar com Valgrind que está certo.

Você precisa entender a diferença entre delete e delete[]
São duas coisas diferentes. O delete[] é para arrays...


6. Re: Arrays multidimensionais, escopo de variável, classes e um problemão [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 05/06/2018 - 15:04h

Alterei a postagem acima (que, como eu então avisara, tive de interromper por ter tido de sair). Peço que a examinem novamente.






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts