Halloweenská akce! Na stránce s dobitím bodů zadej dole kód STRASIDELNYCH20 a získej porci +20% bodů zdarma!
Akce končí 31.10. o půlnoci.

Lekce 5 - Ukazatel this v C++

C a C++ C++ Objektově orientované programování Ukazatel this v C++

Unicorn College ONEbit hosting Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V předchozí lekci, Destruktory a aplikace konstruktorů v C++, jsme dokončili konstruktory a do dnešní lekce jsme si slíbili, že odstraníme škaredé názvy parametrů, které začínají podtržítkem (_). Před parametry jsme museli přidat podtržítko, protože by C++ nevědělo, ke které proměnné se příkaz vztahuje (zda k atributu nebo k parametru). Ukazatel this nám s tímto problémem pomůže.

Ukazatel

this je klíčové slovo jazyka C++ a nemůžeme vytvořit proměnnou, třídu nebo typ, který by se jmenoval stejně. Jak bylo řečeno, jedná se o ukazatel, který je přístupný ve všech metodách třídy a odkazuje se na samotnou instanci. S touto konstrukcí jazyka bývá často problém, proto začneme zlehka. this je ukazatel na instanci samotnou, musí být tedy stejného typu jako je třída. To lze demonstrovat například u třídy Hrac, kde změníme konstruktor následovně:

Hrac.cpp

#include "Hrac.h"

Hrac::Hrac(string _jmeno)
{
    Hrac const * aktualni = this;
    jmeno = _jmeno;
}

Pokud se pokusíme uložit ukazatel do libovolného jiného typu (například int), potom nám kompilátor zahlásí následující chybu (pro Visual Studio):

error C2440: 'initializing': cannot convert from 'Hrac *const ' to 'int *'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast

To znamená, že nelze převést ukazatel typu Hrac * const na typ int *. Zároveň nám tím kompilátor prozrazuje typ ukazatele this. Jedná se o konstantní ukazatel (viz lekce o Konstantních hodnotách). Můžeme měnit instanci, na kterou ukazuje, ale nemůžeme měnit hodnotu ukazatele (const za hvězdičkou). Následující kód tedy nebude validní:

this = new Hrac("Karel");

Kompilace zahlásí:

error C2106: '=': left operand must be l-value

Tím máme vyřešeno, co to vlastně this je. Nyní ještě musíme vyřešit, na co ukazuje.

Příklad s metodou hod()

Jak bylo řečeno, ukazuje na instanci samotnou. Pro příklad si upravíme metodu hod() na třídě Kostka tak, aby přijímala jako parametr ukazatel na typ Kostka:

Kostka.h

class Kostka
{
public:
    Kostka();
    Kostka(int _pocet_sten);
    ~Kostka();
    int pocet_sten;
    int hod(Kostka* k);
};

Kostka.cpp

// ...předchozí implementace
int Kostka::hod(Kostka* k)
{
    return rand() % pocet_sten + 1;
}

Nyní, když zavoláme v main.cpp metodu hod(), předáme jí ukazatel na samotnou instanci:

// main.cpp
Kostka kostka;
for (int i = 0; i < 10; i++)
    kostka.hod(&kostka);
cout << endl;

A jak si dokážeme, že this odkazuje skutečně na tuto instanci? Porovnáme adresy odkazů - metodu hod() upravíme následovně a program spustíme.

int Kostka::hod(Kostka* k)
{
    cout << "Adresa this:      " << this << endl;
    cout << "Adresa parametru: " << k << endl;
    return rand() % pocet_sten + 1;
}
#include <iostream>
#include "Kostka.h"

using namespace std;
int main()
{
    Kostka kostka;
    for (int i = 0; i < 10; i++)
        kostka.hod(&kostka);
    cout << endl;
}
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
class Kostka
{
public:
    Kostka();
    Kostka(int _pocet_sten);
    ~Kostka();
    int pocet_sten;
    int hod(Kostka* k);
};

Pozn.: Adresy se zřejmě budou lišit, ale dvojice by měla být stejná.

Konzolová aplikace
Adresa this:      0x7ffc781864e0
Adresa parametru: 0x7ffc781864e0

Pokud vytvoříme kostky dvě, budou adresy rozdílné:

int main()
{
    Kostka prvni;
    Kostka druha;
    prvni.hod(&prvni);
    druha.hod(&druha);
}
#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
class Kostka
{
public:
    Kostka();
    Kostka(int _pocet_sten);
    ~Kostka();
    int pocet_sten;
    int hod(Kostka* k);
};
#include <iostream>
#include <cstdlib>
#include <ctime>
#include "Kostka.h"
Kostka::Kostka() : Kostka(6)
{
    cout << "Volani bezparametrickeho konstruktoru" << endl;
}

Kostka::Kostka(int _pocet_sten)
{
    cout << "Volani konstruktoru s parametrem" << endl;
    pocet_sten = _pocet_sten;
    srand(time(NULL));
}

Kostka::~Kostka()
{
    cout << "Volani destruktoru pro kostku s " << pocet_sten << " stenami" << endl;
}
int Kostka::hod(Kostka* k)
{
    cout << "Adresa this:      " << this << endl;
    cout << "Adresa parametru: " << k << endl;
    return rand() % pocet_sten + 1;
}

Konzolová aplikace
Adresa this:      0x7ffe01f06b40
Adresa parametru: 0x7ffe01f06b40
Adresa this:      0x7ffe01f06b30
Adresa parametru: 0x7ffe01f06b30

Zjednodušení názvů parametrů pomocí this

Co z toho plyne? O this můžeme uvažovat jako o ukazateli, který se odkazuje na instanci, pro kterou jsme metodu volali. Tento ukazatel je přístupný ve všech metodách (včetně konstruktorů a destruktorů) a toho my využijeme. Všechny úpravy kódu, které jsme zatím provedli, přepíšeme zpět do původního stavu (nebo postačí stáhnout projekt z minulé lekce).

Teď již můžeme odstranit ty škaredé názvy parametrů. V čem byl problém? Pokud jsme použili parametr se stejným názvem jako je atribut, tento parametr překryl atribut a pracovali jsme pouze s parametrem. Například pro kostku, pokud změníme konstruktor do následující podoby:

Kostka.h

class Kostka
{
public:
    Kostka();
    Kostka(int pocet_sten);
    ~Kostka();
    int pocet_sten;
    int hod();
};

Kostka.cpp

//...zbývající implementace
Kostka::Kostka(int pocet_sten)
{
    cout << "Volani konstruktoru s parametrem" << endl;
    pocet_sten = pocet_sten; //do proměnné, kterou jsme přijali jako parametr, uložíme hodnotu z parametru
    srand(time(NULL));
}

Musíme nějak říci, že chceme použít proměnnou z instance. Ale samotnou instanci přece máme v ukazateli this!

Kostka.cpp

//...zbývající implementace
Kostka::Kostka(int pocet_sten)
{
    cout << "Volani konstruktoru s parametrem" << endl;
    this->pocet_sten = pocet_sten; //do proměnné instance uložíme hodnotu z parametru
    srand(time(NULL));
}

Stejným způsobem upravíme i třídu Arena a Hrac. Tím jsme vlastně hotovi s praktickou částí v této lekci.

Používat nebo nepoužívat this

Do této lekce jsme o ukazateli this nevěděli a přesto jsme mohli měnit atributy tříd. Pokud neexistuje proměnná (nemusí se nutně jednat o parametr), který má stejný název jako atribut, this používat nemusíme (ale můžeme). Některé jazyky (jako Java nebo C#) pracují stejně jako C++ a nevyžadují použití this, pokud to není nutné. Naopak jiné jazyky (například PHP nebo Python) vyžadují, aby byl ukazatel pro přístup k atributu vždy použit. V C++ můžeme například destruktor arény napsat dvěma způsoby a oba budou fungovat.

Arena::~Arena()
{
    for (int i = 0; i < pocet_hracu; i++)
        delete hraci[i];
    delete[] hraci;
    hraci = NULL;
}

Arena::~Arena()
{
    for (int i = 0; i < this->pocet_hracu; i++)
        delete hraci[i];
    delete[] hraci;
    hraci = NULL;
}

Kterou variantu používat není přesně dáno a je na každém programátorovi, aby si zvolil. Osobně upřednostňuji druhou variantu (i když je delší), protože zřetelně vyjadřuje použití atributu na třídě. Proto tento zápis budu používat i dále v tutoriálu (ale není nutný).

Stejně jako můžeme přistupovat k atributům, můžeme i volat metody instance. Například pokud bychom chtěli z konstruktoru (z jakéhokoliv důvodu), zavolat metodu hod(), můžeme to udělat pouze názvem metody, nebo pomocí this. Oba přístupy jsou demonstrovány:

Kostka::Kostka(int pocet_sten)
{
    cout << "Volani konstruktoru s parametrem" << endl;
    this->pocet_sten = pocet_sten; // zde this být musí, protože máme parametr se stejným jménem
    srand(time(NULL));
    hod(); // zde již ne, protože "hod" není nikde překryto
}

Kostka::Kostka(int pocet_sten)
{
    cout << "Volani konstruktoru s parametrem" << endl;
    this->pocet_sten = pocet_sten;
    srand(time(NULL));
    this->hod();
}

Tím je dnešní lekce kompletní. V lekci příští, Bojovník do arény - Zapouzdření, si do arény vytvoříme bojovníky.


 

Stáhnout

Staženo 20x (7.22 kB)
Aplikace je včetně zdrojových kódů v jazyce C++

 

 

Článek pro vás napsal patrik.valkovic
Avatar
Jak se ti líbí článek?
3 hlasů
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity (7)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!