paulo1205
(usa Ubuntu)
Enviado em 12/06/2018 - 01:59h
Não considero POG. É uma maneira de fazer mais eficiente em espaço e em tempo, por alocar (e gastar espaço de gerenciamento para) apenas 2 ponteiros, em vez de N+1. Você até tem os N+1 ponteiros, mas N-1 deles reaproveitam pedaços da segunda alocação.
Nessa ideia, apenas duas coisas me incomodam, uma muito pouco, e outra um bocado mais. O que me incomoda pouco é a ligeira assimetria, que se deve ao fato de a primeira “linha” da matriz ser construída de um jeito, e as demais, de outro.
A segunda coisa, que me incomoda mais, é a falta de encapsulamento. Como você está mostrando o “como fazer”, não há muito como fugir disso. Mas ter esses ponteiros todos visíveis é algo que provavelmente seria melhor evitar.
Para encapsular, eu pensei na seguinte ideia em C++. Note que em momento nenhum eu devolvo um objeto que possa ser usado para chegar ao ponteiro alocado para conter os dados (isto é: nem como ponteiro diretamente, nem como referência direta para algum elemento do
array usado internamente — especialmente o primeiro elemento da primeira linha, cujo endereço é igual ao do
array). Para isso eu tive de criar um tipo para encapsular ponteiros para as linhas, e outro para encapsular elementos individuais, bem como os devidos operadores.
#include <iostream>
#include <cstddef>
class matriz {
private:
size_t _lins, _cols;
int *_elementos;
class linha {
private:
friend class matriz;
int *_elementos;
size_t _cols;
class elemento {
private:
friend class matriz::linha;
friend std::ostream &operator<<(std::ostream &, const elemento &);
int *_elemento;
elemento(int *p): _elemento(p) { }
elemento(const elemento &) = delete; // Impede construtor de cópia default.
elemento(elemento &&old): elemento(old._elemento) { }
public:
operator int() const { return *_elemento; } // Retorna cópia do inteiro, não referência.
elemento &operator=(int val){
*_elemento=val;
return *this; // Retorna referência para o encapsulador, não para o dado.
}
elemento &operator=(const elemento &outro){
*_elemento=*outro._elemento;
return *this; // Retorna referência para o encapsulador, não para o dado.
}
template <class T> operator T() const { return T(*_elemento); }
};
friend std::ostream &operator<<(std::ostream &, const elemento &);
linha(int *elems_linha, size_t cols): _elementos(elems_linha), _cols(cols) { }
linha(const linha &) = delete;
linha(linha &&old): linha(old._elementos, old._cols) { }
public:
size_t cols(){ return _cols; }
const elemento operator[](size_t col) const { return _elementos+col; }
elemento operator[](size_t col){ return _elementos+col; }
/*
Outros recursos:
- iterator/const_iterator sobre elementos da linha;
- funções begin()/cbegin()/end()/cend() etc.
*/
};
friend std::ostream &operator<<(std::ostream &, const linha::elemento &);
public:
matriz(size_t lins, size_t cols):
_lins(lins), _cols(cols), _elementos(new int [lins*cols])
{
}
virtual ~matriz(){ delete [] _elementos; }
size_t lins() const { return _lins; }
size_t cols() const { return _cols; }
const linha operator[](size_t lin) const { return linha(_elementos+lin*_cols, _cols); }
linha operator[](size_t lin){ return linha(_elementos+lin*_cols, _cols); }
/*
Outros recursos:
- iterator/const_iterator sobre linhas da matriz;
- funções begin()/cbegin()/end()/cend() etc.
*/
};
std::ostream &operator<<(std::ostream &os, const matriz::linha::elemento &e){
return os << int(e);
}
// Utiliza versões que retornam “const matriz::linha” e “const matriz::linha::elemento”.
void print(const matriz &m){
for(size_t l=0; l<m.lins(); l++){
for(size_t c=0; c<m.cols(); c++)
std::cout << '\t' << m[l][c];
std::cout << '\n';
}
}
int main(){
matriz m(3, 4);
int i=0;
for(size_t l=0; l<m.lins(); l++)
for(size_t c=0; c<m.cols(); c++)
m[l][c]=i++;
print(m);
}