Funktiot
Arvo- ja viitesemantiikka
Pythonissa ei ole muuttujia aivan siinä merkityksessä kuin muuttujat perinteisesti ymmärretään. Pythonissa on nimiä, tietoalkioita eli olioita sekä mekanismi, jolla nimi sidotaan olioon. Ohjelmakoodissa olioon voidaan viitata sen nimen avulla. Käytännössä Pythonin sijoitusoperaatiolla (=) nimi sidotaan olioon.
Edellisellä ohjelmointikurssilla tätä nimen ja tietoalkion suhdetta havainnollistettiin esimerkiksi seuraavalla koodilla:
a = 42 b = a c = 42
Nimien a, b ja c avulla voidaan käsitellä samaa arvoa. Tätä tapaa kutsutaan viitesemantiikaksi.
C++:ssa oletus toimii toisin: sijoitus luo arvosta kopion.
int a = 42; int b = a; int c; c = 42;
Ohjelmassa on nyt useita erillisiä kokonaislukumuuttujia, joiden arvona on 42. Tätä mekanismia, jossa alustaminen ja sijoittaminen kopioivat arvon, kutsutaan arvosemantiikaksi.
Arvosemantiikkaa on helpompi ymmärtää, kun muuttujaa ajatellaan nimettynä keskusmuistin alueena:
- Muuttuja on tietylle muistialueelle annettu nimi, ei jollekin tietoalkiolle annettu lisänimi.
- Sijoitus ja alustus asettavat tai korvaavat muistialueella olevan arvon.
Termi muuttuja tulee siitä, että muuttujan nimeämän muistialueen sisältö voi muuttua. Pythonissa nimi puolestaan voidaan suorituksen aikana sitoa eri olioihin.
C++:n muuttujia voi havainnollistaa myös muistipaikkoina:
Muistipaikkojen sijoittelua voi tarkastella myös toteutusteknisemmin:
Lokerot kuvaavat keskusmuistin alueita, joihin tietoa voidaan tallentaa.
Arvosemantiikka ja kokoelmat
Viite- ja arvosemantiikan ero näkyy erityisen hyvin, kun käsitellään kokoelmia. Pythonissa listaa vastaa tässä esimerkissä C++:n deque-kirjastotyyppi:
Python
def main(): varasto1 = [3, 9, 27, 81, 243] varasto2 = varasto1 varasto2[3] = 0 print(varasto1[3], varasto2[3]) main()
C++
#include <iostream> #include <deque> using namespace std; int main() { deque<int> varasto1 = {3, 9, 27, 81, 243}; deque<int> varasto2; varasto2 = varasto1; // Kopioidaan varasto1 varasto2:een varasto2.at(3) = 0; // at vastaa tässä Pythonin hakasulkeita cout << varasto1.at(3) << " " << varasto2.at(3) << endl; }
C++:ssa uuden deque-olion määrittelyssä ilmoitetaan kulmasulkeissa, minkä tyyppisiä alkioita kokoelma sisältää. Tässä molemmat kokoelmat sisältävät kokonaislukuja.
Python tulostaa 0 0, mutta C++ tulostaa 81 0. C++:n sijoitus varasto2 = varasto1 kopioi kokoelman. Kun kopiosta muutetaan alkiota, alkuperäinen varasto1 ei muutu.
Pythonissa varasto1 ja varasto2 ovat kaksi nimeä samalle listalle. Siksi listan muuttaminen kumman tahansa nimen kautta näkyy molempien kautta.
Viitteet C++:ssa
C++:ssa samalle muuttujalle voi antaa lisänimen viitteen avulla. Viite määritellään kirjoittamalla & tyypin ja viitteen nimen väliin. Viite on alustettava sillä muuttujalla, johon sen halutaan viittaavan:
kohdetyyppi& viitteen_nimi = kohdemuuttuja;
Edellisen esimerkin kokoelmia voidaan käsitellä myös viitteen avulla:
#include <iostream> #include <deque> using namespace std; int main() { deque<int> varasto1 = {3, 9, 27, 81, 243}; deque<int>& varasto2 = varasto1; varasto2[3] = 0; cout << varasto1[3] << " " << varasto2[3] << endl; }
Tämä versio toimii Python-esimerkin tavoin ja tulostaa 0 0, koska varasto2 on viite samaan kokoelmaan kuin varasto1.
Viitteistä tulee hyödyllisiä, kun niitä käytetään funktion parametrien tyyppeinä. Tällöin funktio voi käsitellä suoraan parametrina annettua muuttujaa sen kopion sijasta.
Välilyönnit viitteen määrittelyssä
&-merkin ympärillä olevat välilyönnit voivat vaihdella, eikä merkitystä ole sillä, onko merkki kiinni tyypissä vai viitteen nimessä. Selkeyden vuoksi kannattaa käyttää muotoa, jossa merkki on tyypin yhteydessä:
int kokonaisluku = 42; int& viite1 = kokonaisluku; int &viite2 = kokonaisluku; int & viite3 = kokonaisluku;
Kertaa: Arvo- ja viitesemantiikka
Mitä tavallinen arvoparametri saa funktion kutsussa?
Miten C++:n viiteparametri merkitään tyypissä?