IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 5 - Ukazatel this v C++

V předešlém cvičení, Řešené úlohy k 3. a 4. lekci OOP v C++, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

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 následujícím cvičení, Řešené úlohy k 5. lekci OOP v C++, si procvičíme nabyté zkušenosti z předchozích lekcí.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

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

 

Předchozí článek
Řešené úlohy k 3. a 4. lekci OOP v C++
Všechny články v sekci
Objektově orientované programování v C++
Přeskočit článek
(nedoporučujeme)
Řešené úlohy k 5. lekci OOP v C++
Článek pro vás napsal Patrik Valkovič
Avatar
Uživatelské hodnocení:
34 hlasů
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity