Buscar..
Introducción
Las matrices son elementos del mismo tipo que se colocan en ubicaciones de memoria contiguas. Los elementos pueden ser referenciados individualmente por un identificador único con un índice agregado.
Esto le permite declarar múltiples valores de variables de un tipo específico y acceder a ellos individualmente sin necesidad de declarar una variable para cada valor.
Tamaño de matriz: tipo seguro en tiempo de compilación.
#include <stddef.h> // size_t, ptrdiff_t
//----------------------------------- Machinery:
using Size = ptrdiff_t;
template< class Item, size_t n >
constexpr auto n_items( Item (&)[n] ) noexcept
-> Size
{ return n; }
//----------------------------------- Usage:
#include <iostream>
using namespace std;
auto main()
-> int
{
int const a[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
Size const n = n_items( a );
int b[n] = {}; // An array of the same size as a.
(void) b;
cout << "Size = " << n << "\n";
}
El lenguaje C para tamaño de matriz, sizeof(a)/sizeof(a[0])
, aceptará un puntero como argumento y, por lo general, dará un resultado incorrecto.
Para C ++ 11
usando C ++ 11 puedes hacer:
std::extent<decltype(MyArray)>::value;
Ejemplo:
char MyArray[] = { 'X','o','c','e' };
const auto n = std::extent<decltype(MyArray)>::value;
std::cout << n << "\n"; // Prints 4
Hasta C ++ 17 (a partir de este escrito) C ++ no tenía un lenguaje central incorporado ni una utilidad de biblioteca estándar para obtener el tamaño de una matriz, pero esto puede implementarse pasando la matriz por referencia a una plantilla de función, como mostrado anteriormente. Punto fino pero importante: el parámetro de tamaño de la plantilla es un size_t
, algo inconsistente con el tipo de resultado de la función Size
con signo, para acomodar el compilador g ++ que a veces insiste en size_t
para la coincidencia de la plantilla.
Con C ++ 17 y versiones posteriores, se puede usar std::size
, que está especializado para arreglos.
Matriz en bruto de tamaño dinámico
// Example of raw dynamic size array. It's generally better to use std::vector.
#include <algorithm> // std::sort
#include <iostream>
using namespace std;
auto int_from( istream& in ) -> int { int x; in >> x; return x; }
auto main()
-> int
{
cout << "Sorting n integers provided by you.\n";
cout << "n? ";
int const n = int_from( cin );
int* a = new int[n]; // ← Allocation of array of n items.
for( int i = 1; i <= n; ++i )
{
cout << "The #" << i << " number, please: ";
a[i-1] = int_from( cin );
}
sort( a, a + n );
for( int i = 0; i < n; ++i ) { cout << a[i] << ' '; }
cout << '\n';
delete[] a;
}
Un programa que declara una matriz T a[n];
donde n
se determina un tiempo de ejecución, se puede compilar con ciertos compiladores que admiten matrices de longitud variable ( VLA) C99 como una extensión de lenguaje. Pero los VLA no son compatibles con C ++ estándar. Este ejemplo muestra cómo asignar manualmente una matriz de tamaño dinámico a través de una new[]
expresión new[]
,
int* a = new int[n]; // ← Allocation of array of n items.
... luego utilícelo, y finalmente deséchelo mediante una delete[]
-expresión:
delete[] a;
La matriz asignada aquí tiene valores indeterminados, pero se puede inicializar con cero simplemente agregando un paréntesis vacío ()
, así: new int[n]()
. Más generalmente, para un tipo de elemento arbitrario, esto realiza una inicialización de valores .
Como parte de una función en una jerarquía de llamadas, este código no sería seguro de excepción, ya que una excepción antes de la expresión delete[]
(y después de la new[]
) causaría una pérdida de memoria. Una forma de abordar ese problema es automatizar la limpieza, por ejemplo, mediante un puntero inteligente std::unique_ptr
. Pero una forma generalmente mejor de abordarlo es simplemente usar std::vector
: para eso está std::vector
.
Expandiendo la matriz de tamaño dinámico usando std :: vector.
// Example of std::vector as an expanding dynamic size array.
#include <algorithm> // std::sort
#include <iostream>
#include <vector> // std::vector
using namespace std;
int int_from( std::istream& in ) { int x = 0; in >> x; return x; }
int main()
{
cout << "Sorting integers provided by you.\n";
cout << "You can indicate EOF via F6 in Windows or Ctrl+D in Unix-land.\n";
vector<int> a; // ← Zero size by default.
while( cin )
{
cout << "One number, please, or indicate EOF: ";
int const x = int_from( cin );
if( !cin.fail() ) { a.push_back( x ); } // Expands as necessary.
}
sort( a.begin(), a.end() );
int const n = a.size();
for( int i = 0; i < n; ++i ) { cout << a[i] << ' '; }
cout << '\n';
}
std::vector
es una plantilla de clase de biblioteca estándar que proporciona la noción de una matriz de tamaño variable. Se encarga de toda la administración de la memoria, y el búfer es contiguo, por lo que se puede pasar un puntero al búfer (por ejemplo, &v[0]
o v.data()
) a las funciones de API que requieren una matriz sin formato. Incluso se puede expandir un vector
en tiempo de ejecución, por ejemplo, a través de la función miembro push_back
que agrega un elemento.
La complejidad de la secuencia de n operaciones push_back
, incluida la copia o el movimiento involucrado en las expansiones vectoriales, se amortiza O ( n ). "Amortizado": en promedio.
Internamente, esto generalmente se logra cuando el vector duplica su tamaño de búfer, su capacidad, cuando se necesita un búfer más grande. Por ejemplo, para un búfer que comienza como tamaño 1 y se duplica repetidamente según sea necesario para n = 17 llamadas push_back
, esto implica 1 + 2 + 4 + 8 + 16 = 31 operaciones de copia, que es menos de 2 × n = 34. Y más generalmente, la suma de esta secuencia no puede exceder de 2 × n .
En comparación con el ejemplo de matriz sin formato de tamaño dinámico, este código basado en vector
no requiere que el usuario suministre (y sepa) el número de elementos por adelantado. En su lugar, el vector se expande según sea necesario, para cada nuevo valor de elemento especificado por el usuario.
Una matriz de matriz sin formato de tamaño fijo (es decir, una matriz sin formato 2D).
// A fixed size raw array matrix (that is, a 2D raw array).
#include <iostream>
#include <iomanip>
using namespace std;
auto main() -> int
{
int const n_rows = 3;
int const n_cols = 7;
int const m[n_rows][n_cols] = // A raw array matrix.
{
{ 1, 2, 3, 4, 5, 6, 7 },
{ 8, 9, 10, 11, 12, 13, 14 },
{ 15, 16, 17, 18, 19, 20, 21 }
};
for( int y = 0; y < n_rows; ++y )
{
for( int x = 0; x < n_cols; ++x )
{
cout << setw( 4 ) << m[y][x]; // Note: do NOT use m[y,x]!
}
cout << '\n';
}
}
Salida:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
C ++ no admite sintaxis especial para indexar una matriz multidimensional. En su lugar, una matriz de este tipo se ve como una matriz de matrices (posiblemente de matrices, etc.), y se utiliza la notación de índice único ordinario [
i
]
para cada nivel. En el ejemplo anterior, m[y]
refiere a la fila y
de m
, donde y
es un índice basado en cero. Entonces esta fila puede ser indexado a su vez, por ejemplo, m[y][x]
, que se refiere a la x
ésimo elemento - o columna - de fila y
.
Es decir, el último índice varía más rápidamente, y en la declaración, el rango de este índice, que es el número de columnas por fila, es el último y el tamaño "más interno" especificado.
Como C ++ no proporciona soporte integrado para matrices de tamaño dinámico, aparte de la asignación dinámica, una matriz de tamaño dinámico a menudo se implementa como una clase. Luego, la notación de indexación de matriz sin procesar m[y][x]
tiene algún costo, ya sea al exponer la implementación (de modo que, por ejemplo, una vista de una matriz transpuesta se vuelve prácticamente imposible) o al agregar un poco de sobrecarga y pequeños inconvenientes cuando se hace al regresar un objeto proxy del operator[]
. Y así, la notación de indexación para tal abstracción puede y será generalmente diferente, tanto en el aspecto como en el orden de los índices, por ejemplo, m(x,y)
o m.at(x,y)
o m.item(x,y)
.
Una matriz de tamaño dinámico utilizando std :: vector para almacenamiento.
Desafortunadamente, a partir de C ++ 14 no hay una clase de matriz de tamaño dinámico en la biblioteca estándar de C ++. Clases de matriz que apoyan el tamaño dinámico son sin embargo disponibles a partir de una serie de bibliotecas 3 ª parte, incluyendo la biblioteca Boost Matrix (una sub-biblioteca dentro de la biblioteca Boost).
Si no desea una dependencia en Boost o alguna otra biblioteca, entonces la matriz de tamaño dinámico de un hombre pobre en C ++ es como
vector<vector<int>> m( 3, vector<int>( 7 ) );
… Donde vector
es std::vector
. La matriz se crea aquí copiando un vector de fila n veces donde n es el número de filas, aquí 3. Tiene la ventaja de proporcionar la misma notación de indexación m[y][x]
que para una matriz de matriz sin formato de tamaño fijo, pero es un poco ineficiente porque implica una asignación dinámica para cada fila, y es un poco inseguro porque es posible cambiar inadvertidamente el tamaño de una fila.
Un enfoque más seguro y eficiente es usar un solo vector como almacenamiento para la matriz y asignar el código del cliente ( x , y ) a un índice correspondiente en ese vector:
// A dynamic size matrix using std::vector for storage.
//--------------------------------------------- Machinery:
#include <algorithm> // std::copy
#include <assert.h> // assert
#include <initializer_list> // std::initializer_list
#include <vector> // std::vector
#include <stddef.h> // ptrdiff_t
namespace my {
using Size = ptrdiff_t;
using std::initializer_list;
using std::vector;
template< class Item >
class Matrix
{
private:
vector<Item> items_;
Size n_cols_;
auto index_for( Size const x, Size const y ) const
-> Size
{ return y*n_cols_ + x; }
public:
auto n_rows() const -> Size { return items_.size()/n_cols_; }
auto n_cols() const -> Size { return n_cols_; }
auto item( Size const x, Size const y )
-> Item&
{ return items_[index_for(x, y)]; }
auto item( Size const x, Size const y ) const
-> Item const&
{ return items_[index_for(x, y)]; }
Matrix(): n_cols_( 0 ) {}
Matrix( Size const n_cols, Size const n_rows )
: items_( n_cols*n_rows )
, n_cols_( n_cols )
{}
Matrix( initializer_list< initializer_list<Item> > const& values )
: items_()
, n_cols_( values.size() == 0? 0 : values.begin()->size() )
{
for( auto const& row : values )
{
assert( Size( row.size() ) == n_cols_ );
items_.insert( items_.end(), row.begin(), row.end() );
}
}
};
} // namespace my
//--------------------------------------------- Usage:
using my::Matrix;
auto some_matrix()
-> Matrix<int>
{
return
{
{ 1, 2, 3, 4, 5, 6, 7 },
{ 8, 9, 10, 11, 12, 13, 14 },
{ 15, 16, 17, 18, 19, 20, 21 }
};
}
#include <iostream>
#include <iomanip>
using namespace std;
auto main() -> int
{
Matrix<int> const m = some_matrix();
assert( m.n_cols() == 7 );
assert( m.n_rows() == 3 );
for( int y = 0, y_end = m.n_rows(); y < y_end; ++y )
{
for( int x = 0, x_end = m.n_cols(); x < x_end; ++x )
{
cout << setw( 4 ) << m.item( x, y ); // ← Note: not `m[y][x]`!
}
cout << '\n';
}
}
Salida:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
El código anterior no es de grado industrial: está diseñado para mostrar los principios básicos y satisfacer las necesidades de los estudiantes que aprenden C ++.
Por ejemplo, uno puede definir sobrecargas de operator()
para simplificar la notación de indexación.
Inicialización de matriz
Una matriz es solo un bloque de ubicaciones de memoria secuencial para un tipo específico de variable. Las matrices se asignan de la misma manera que las variables normales, pero con corchetes anexados a su nombre []
que contienen el número de elementos que caben en la memoria de la matriz.
El siguiente ejemplo de una matriz utiliza el tipo int
, el nombre variable arrayOfInts
y el número de elementos [5]
que la matriz tiene espacio:
int arrayOfInts[5];
Una matriz se puede declarar e inicializar al mismo tiempo así
int arrayOfInts[5] = {10, 20, 30, 40, 50};
Al inicializar una matriz al enumerar todos sus miembros, no es necesario incluir el número de elementos dentro de los corchetes. Será calculado automáticamente por el compilador. En el siguiente ejemplo, es 5:
int arrayOfInts[] = {10, 20, 30, 40, 50};
También es posible inicializar solo los primeros elementos mientras se asigna más espacio. En este caso, es obligatorio definir la longitud entre paréntesis. Lo siguiente asignará una matriz de longitud 5 con inicialización parcial, el compilador inicializa todos los elementos restantes con el valor estándar del tipo de elemento, en este caso cero.
int arrayOfInts[5] = {10,20}; // means 10, 20, 0, 0, 0
Las matrices de otros tipos de datos básicos pueden inicializarse de la misma manera.
char arrayOfChars[5]; // declare the array and allocate the memory, don't initialize
char arrayOfChars[5] = { 'a', 'b', 'c', 'd', 'e' } ; //declare and initialize
double arrayOfDoubles[5] = {1.14159, 2.14159, 3.14159, 4.14159, 5.14159};
string arrayOfStrings[5] = { "C++", "is", "super", "duper", "great!"};
También es importante tener en cuenta que al acceder a los elementos de la matriz, el índice de elementos de la matriz (o posición) comienza desde 0.
int array[5] = { 10/*Element no.0*/, 20/*Element no.1*/, 30, 40, 50/*Element no.4*/};
std::cout << array[4]; //outputs 50
std::cout << array[0]; //outputs 10