Sök…


Introduktion

Matriser är element av samma typ som placeras i angränsande minnesplatser. Elementen kan refereras individuellt av en unik identifierare med ett tillagt index.

Detta gör att du kan deklarera flera variabla värden av en specifik typ och få åtkomst till dem individuellt utan att behöva deklarera en variabel för varje värde.

Matrisstorlek: typ säker vid kompileringstid.

#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";
}

C-formspråket för sizeof(a)/sizeof(a[0]) , sizeof(a)/sizeof(a[0]) , accepterar en pekare som argument och ger då i allmänhet ett felaktigt resultat.

För C ++ 11

med C ++ 11 kan du göra:

std::extent<decltype(MyArray)>::value;

Exempel:

char MyArray[] = { 'X','o','c','e' };
const auto n = std::extent<decltype(MyArray)>::value;
std::cout << n << "\n"; // Prints 4

Fram till C ++ 17 (kommande från och med detta skrivande) hade C ++ inget inbyggt kärnspråk eller standardbiblioteksverktyg för att få storleken på en matris, men detta kan implementeras genom att skicka matrisen med referens till en funktionsmall, som visat ovan. Fin men viktig punkt: mall storlek parametern är en size_t , något inkonsekvent med det undertecknade Size funktionen resultat typ, för att rymma g ++ kompilator som ibland insisterar på size_t för mallmatchning.

Med C ++ 17 och senare kan man istället använda std::size , som är specialiserat för matriser.

Rå matris med dynamisk storlek

// 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;
}

Ett program som deklarerar en matris T a[n]; där n bestäms en körtid, kan kompilera med vissa kompilatorer som stöder C99- variadiska längd-arrayer (VLA) som språkförlängning. Men VLA stöds inte av standard C ++. Detta exempel visar hur man manuellt kan tilldela en dynamisk storleksgrupp via en new[] -uttryck,

int*        a   = new int[n];       // ← Allocation of array of n items.

... använd det sedan och dela upp det till slut via en delete[] -uttryck:

delete[] a;

Arrayen som tilldelas här har obestämda värden, men den kan initialiseras noll genom att bara lägga till en tom parentes () , så här: new int[n]() . Mer generellt, för godtycklig artikeltyp, utför detta en värdinitialisering .

Som en del av en funktion nere i ett samtalshierarki skulle denna kod inte vara undantagssäker, eftersom ett undantag före delete[] (och efter det new[] ) skulle orsaka en minnesläcka. Ett sätt att ta itu med det problemet är att automatisera saneringen via t.ex. en std::unique_ptr smart pointer. Men ett generellt bättre sätt att hantera det är att bara använda en std::vector : det är vad std::vector finns där för.

Utöka dynamisk storlek med hjälp av 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 är en standardmall för biblioteksklass som ger uppfattningen om en matris med variabel storlek. Den tar hand om all minneshantering och bufferten är sammanhängande så att en pekare till bufferten (t.ex. &v[0] eller v.data() ) kan skickas till API-funktioner som kräver en rå matris. En vector kan till och med utökas vid körning, t.ex. genom push_back medlemsfunktionen som lägger till ett objekt.

Komplexiteten i sekvensen för n push_back operationer, inklusive kopiering eller rörelse involverad i vektorutvidgningarna, amorteras O ( n ). "Amortiserad": i genomsnitt.

Internt uppnås detta vanligtvis genom att vektorn fördubblar sin buffertstorlek, dess kapacitet, när en större buffert behövs. Exempelvis för en buffert som börjar som storlek 1 och som upprepade gånger fördubblas efter behov för n = 17 push_back samtal innebär detta 1 + 2 + 4 + 8 + 16 = 31 kopieringsoperationer, vilket är mindre än 2 × n = 34. Och mer generellt kan summan av denna sekvens inte överstiga 2 × n .

Jämfört med exemplet med den råa matrisen med dynamisk storlek, kräver inte denna vector kod användaren att ange (och veta) antalet objekt framför. I stället utvidgas vektorn bara efter behov för varje nytt objektvärde som anges av användaren.

En rå matris med fast storlek (det vill säga en rå matris med 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';
    }
}

Produktion:

   1   2   3   4   5   6   7
   8   9  10  11  12  13  14
  15  16  17  18  19  20  21

C ++ stöder inte speciell syntax för indexering av en flerdimensionell matris. Istället ses en sådan matris som en matris med matriser (eventuellt av matriser, och så vidare), och den vanliga enstaka indexnotationen [ i ] används för varje nivå. I exemplet ovan hänvisar m[y] till rad y av m , där y är ett nollbaserat index. Då kan denna rad indexeras i tur och ordning, t ex m[y][x] , som hänvisar till det x e objektet - eller kolumnen - i rad y .

Det vill säga det sista indexet varierar snabbast, och i deklarationen är intervallet för detta index, som här är antalet kolumner per rad, den sista och "innersta" storleken som anges.

Eftersom C ++ inte ger inbyggt stöd för dynamiska storlekar, annat än dynamisk allokering, implementeras ofta en dynamisk storleksmatris som en klass. Då har den råa matrisindexeringsnotationen m[y][x] del kostnader, antingen genom att exponera implementeringen (så att t.ex. en vy av en transponerad matris blir praktiskt taget omöjlig) eller genom att lägga till en del omkostnader och lätt besvär när det görs genom att återvända ett proxyobjekt från operator[] . Och indexeringsnotationen för en sådan abstraktion kan och kommer vanligtvis att vara annorlunda, både i utseende och i ordning på index, t.ex. m(x,y) eller m.at(x,y) eller m.item(x,y) .

En matris med dynamisk storlek med std :: vektor för lagring.

Tyvärr finns det från C ++ 14 ingen matrisklass i dynamisk storlek i C ++ -biblioteket. Matrisklasser som stöder dynamisk storlek är emellertid tillgängliga från ett antal tredje partibibliotek, inklusive Boost Matrix-biblioteket (ett underbibliotek inom Boost-biblioteket).

Om du inte vill ha ett beroende av Boost eller något annat bibliotek, är en fattig mans matris med dynamisk storlek i C ++ precis som

vector<vector<int>> m( 3, vector<int>( 7 ) );

... där vector är std::vector . Matrisen skapas här genom att kopiera en radvektor n gånger där n är antalet rader, här 3. Det har fördelen att tillhandahålla samma m[y][x] indexeringsnotation som för en fast rå matrismatris, men det är lite ineffektivt eftersom det innebär en dynamisk allokering för varje rad och det är lite osäkert eftersom det är möjligt att oavsiktligt ändra storlek på en rad.

En mer säker och effektiv metod är att använda en enda vektor som lagring för matrisen och kartlägga klientkodens ( x , y ) till ett motsvarande index i den vektorn:

// 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';
    }
}

Produktion:

   1   2   3   4   5   6   7
   8   9  10  11  12  13  14
  15  16  17  18  19  20  21

Ovanstående kod är inte industriell klass: den är utformad för att visa de grundläggande principerna och tjäna behoven hos elever som lär sig C ++.

Till exempel kan man definiera överbelastningar av operator() att förenkla indexeringsnotationen.

Arrayinitialisering

En matris är bara ett block av sekvensiella minnesplatser för en specifik typ av variabel. Matriser tilldelas på samma sätt som normala variabler, men med fyrkantiga parenteser bifogade dess namn [] som innehåller antalet element som passar in i arrayminnet.

Följande exempel på en matris använder typ int , variabelnamnet arrayOfInts och antalet element [5] som arrayen har utrymme för:

int arrayOfInts[5];

En matris kan deklareras och initialiseras på samma gång

int arrayOfInts[5] = {10, 20, 30, 40, 50};

När man initierar en matris genom att lista alla sina medlemmar, är det inte nödvändigt att inkludera antalet element inuti de fyrkantiga parenteserna. Det kommer att beräknas automatiskt av kompilatorn. I följande exempel är det 5:

int arrayOfInts[] = {10, 20, 30, 40, 50};

Det är också möjligt att initialisera endast de första elementen medan du tilldelar mer utrymme. I detta fall är det obligatoriskt att definiera längden inom parentes. Följande kommer att tilldela en matris med längd 5 med partiell initialisering, kompilatorn initialiserar alla återstående element med standardvärdet för elementtypen, i detta fall noll.

int arrayOfInts[5] = {10,20}; // means 10, 20, 0, 0, 0

Matriser av andra grundläggande datatyper kan initialiseras på samma sätt.

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!"};

Det är också viktigt att notera att när du kommer till arrayelement börjar matrisens elementindex (eller position) från 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


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow