Operace s ukazateli. Co to znamená v C: co je ukazatel Co je uloženo v proměnné typu ukazatel

Ukazatel je odvozený typ, který představuje adresu nějaké hodnoty. Jazyk C++ využívá koncept proměnných adres. Práci s adresami zdědilo C++ z jazyka C. Předpokládejme, že v programu je definována proměnná typu int:

int x;

Můžete definovat proměnnou ukazatele na celé číslo:

int* xptr;

a přiřaďte proměnné xptr adresu proměnné x:

xptr =

Je třeba si pamatovat:

  • Operace & použitá na proměnnou je operace adresy.
  • Operace * použitá na adresu (jinými slovy, aplikovaná na ukazatel) je operace s adresou.

Pro předchozí definice x a y jsou tedy dva operátory ekvivalentní:

// přiřadí proměnné y hodnotu x

int y = x;

// přiřadí proměnné y hodnotu

// umístěný na xptr

int y = *xptr;

Pomocí operace adresy můžete zapisovat hodnoty:

// napište číslo 10 na adresu xptr

*xptr = 10;

Po provedení tohoto příkazu bude hodnota proměnné x 10, protože xptr ukazuje na proměnnou x.

Ukazatel není jen adresa, ale adresa hodnoty určitého typu. Ukazatel xptr je adresa celočíselné hodnoty. Adresy množství jiných typů můžete definovat následovně:

// ukazatel na celé číslo bez znaménka

nesignováno dlouhý* lPtr;

// ukazatel na byte

char* cp;

// ukazatel na objekt třídy Complex

Komplexní* p;

Pokud ukazatel odkazuje na objekt nějaké třídy, pak operace přístupu k atributu třídy místo tečky je označena „->“, například p->real. Pokud si vzpomenete na jeden z předchozích příkladů:

void Complex::Add(Complex x)

This->real = this->real + x.real;

Toto->imaginární = toto->imaginární +

X.imaginární;

pak se jedná o ukazatel na aktuální objekt, tzn. objekt, který provádí metodu Add. Záznam this-> znamená přístup k atributu aktuálního objektu.

Můžete definovat ukazatel na jakýkoli typ, včetně funkce nebo metody třídy. Pokud existuje několik funkcí stejného typu:

int foo(long x);

int bar (dlouhy x);

Můžete definovat proměnnou typu ukazatel na funkci a volat tyto funkce ne přímo, ale nepřímo prostřednictvím ukazatele:

int (*functptr)(dlouhé x);

functionptr =

(*funkptr)(2);

functionptr =

(*funkptr)(4);

K čemu jsou ukazatele? Ukazatele se objevily především pro potřeby programování systému. Jelikož byl jazyk C určen pro „nízkoúrovňové“ programování, bylo nutné přistupovat např. k registrům zařízení. Tyto registry zařízení mají velmi specifické adresy, tzn. bylo nutné číst nebo zapisovat hodnotu na konkrétní adrese. Díky mechanismu ukazatele takové operace nevyžadují žádné další jazykové nástroje.

int* hardwareRegiste =0x80000;

*registrace hardwaru =12;

Použití ukazatelů se však neomezuje pouze na potřeby programování systému. Ukazatele mohou řadu operací výrazně zjednodušit a zrychlit. Předpokládejme, že program má paměťovou oblast pro ukládání mezivýsledků výpočtů. Tuto oblast paměti využívají různé programové moduly. Namísto kopírování této oblasti paměti pokaždé, když přistupujeme k modulu, můžeme předat ukazatel jako argument volání funkce, a tím zjednodušit a urychlit výpočty.

Poslední aktualizace: 27.05.2017

Ukazatele v jazyce C podporují řadu operací: přiřazení, získání adresy ukazatele, získání hodnoty z ukazatele, některé aritmetické operace a porovnávací operace.

Úkol

Ukazatel lze přiřadit buď adresu objektu stejného typu, hodnotu jiného ukazatele nebo konstantu NULL.

Přiřazení adresy ukazateli již bylo probráno v předchozím tématu. Chcete-li získat adresu objektu, použijte operaci &:

Int a = 10; int *pa = // ukazatel pa ukládá adresu proměnné a

Navíc ukazatel a proměnná musí mít stejný typ, in v tomto případě int.

Přiřazení ukazatele jinému ukazateli:

#zahrnout int main(void) ( int a = 10; int b = 2; int *pa = int *pb = printf("Proměnná a: adresa=%p \t hodnota=%d \n", pa, *pa); printf("Proměnná b: adresa=%p \t hodnota=%d \n", pb, *pb); pa = pb; // nyní ukazatel pa ukládá adresu proměnné b printf("Proměnná b: adresa= %p \ t hodnota=%d \n", pa, *pa); návrat 0; )

Když je ukazatel přiřazen jinému ukazateli, první ukazatel ve skutečnosti začne také ukazovat na stejnou adresu, na kterou ukazuje druhý ukazatel.

Pokud nechceme, aby ukazatel ukazoval na konkrétní adresu, můžeme mu přiřadit podmíněnou hodnotu null pomocí konstanty NULL, která je definována v hlavičkovém souboru stdio.h:

Int *pa = NULL;

Dereference ukazatele

Operace dereferencování ukazatele ve tvaru *jméno_ukazatele umožňuje získat objekt na adrese, která je uložena v ukazateli.

#zahrnout int main(void) ( int a = 10; int *pa = int *pb = pa; *pa = 25; printf("Hodnota na ukazateli pa: %d \n", *pa); // 25 printf(" Hodnota na ukazateli pb: %d \n", *pb); // 25 printf("Hodnota proměnné a: %d \n", a); // 25 návrat 0; )

Prostřednictvím výrazu *pa můžeme získat hodnotu na adrese, která je uložena v ukazateli pa, a prostřednictvím výrazu jako *pa = hodnota můžeme na tuto adresu vložit novou hodnotu.

A protože v tomto případě ukazatel pa ukazuje na proměnnou a, tak když se změní hodnota na adrese, na kterou ukazuje ukazatel, změní se i hodnota proměnné a.

Adresa ukazatele

Ukazatel ukládá adresu proměnné a z této adresy můžeme získat hodnotu této proměnné. Ale navíc ukazatel, jako každá proměnná, má sám o sobě adresu, kde se v paměti nachází. Tuto adresu lze také získat pomocí operace &:

Int a = 10; int *pa = printf("adresa ukazatele=%p \n", &pa); // adresa ukazatele printf("adresa uložená v ukazateli=%p \n", pa); // adresa, která je uložena v ukazateli je adresa proměnné a printf("hodnota na ukazateli=%d \n", *pa); // hodnota na adrese v ukazateli - hodnota proměnné a

Srovnávací operace

Na ukazatele lze použít porovnávací operace > , >=< , <= ,== , != . Операции сравнения применяются только к указателям одного типа и константе NULL . Для сравнения используются номера адресов:

Int a = 10; int b = 20; int *pa = int *pb = if(pa > pb) printf("pa (%p) je větší než pb (%p) \n", pa, pb); else printf("pa (%p) je menší nebo rovno pb (%p) \n", pa, pb);

Výstup konzoly v mém případě:

Pa (0060FEA4) je větší než pb (0060FEA0)

Obsazení

Někdy je potřeba přiřadit ukazatel jednoho typu k hodnotě ukazatele jiného typu. V tomto případě byste měli provést operaci převodu typu:

Char c = "N"; char *pc = int *pd = (int *)pc; printf("pc=%p \n", pc); printf("pd=%p\n", pd);

Při učení C mají začátečníci často otázky týkající se ukazatelů, myslím, že všichni mají přibližně stejné otázky, proto popíšu ty, které mi vyvstaly.

K čemu je ukazatel?

Proč se vždy píše „ukazatel typu“ a co je ukazatel typu uint16_t liší od typu ukazatel uint8_t?

A kdo vůbec přišel s indexem?

Než odpovíme na tyto otázky, připomeňme si, co je ukazatel.
Ukazatel je proměnná, která obsahuje adresu nějakého datového prvku (proměnná, konstanta, funkce, struktura).

Chcete-li deklarovat proměnnou jako ukazatel, musíte jejímu názvu předcházet s * a k získání adresy proměnné se používá & (operátor unární adresy).
char a = "a"; char *p =
V tomto případě p obsahuje adresu proměnné a. Ale co je zajímavé pro další práci s ukazatelem není potřeba psát hvězdičku, je potřeba pouze při deklaraci.
char a = "a"; char b = "b"; char *p = p =
V tomto případě p obsahuje adresu proměnné b, ale pokud chceme získat hodnotu umístěnou na této adrese, pak musíme použít operátor dereference, stejná hvězdička *.
char new_simbol = 0; char a = "a"; char *p = new_simbol = *p;
Proměnná new_simbol tedy bude obsahovat ascii kód symbolu "a".

Nyní přejděme přímo k otázkám, k čemu je ukazatel potřeba. Představte si, že máme pole, se kterým chceme ve funkci pracovat. Abyste předali pole funkci, musíte jej zkopírovat, tedy plýtvat pamětí, které už MK má málo, takže správnějším řešením by bylo pole nekopírovat, ale předat adresu jeho první prvek a velikost.
m = (1,2,3...);
Můžete to udělat takto
void foo(char *m, velikost uint8_t) ( )
nebo tak
void foo(char m, velikost uint8_t) ( )
Protože název pole obsahuje adresu jeho prvního prvku, není to nic jiného než ukazatel. Pole můžete procházet pomocí toho nejjednoduššího aritmetické operace Chcete-li například získat hodnotu pátého prvku pole, musíte k adrese pole přidat 4 (adresa prvního prvku) a použít operátor dereference.
m = *(m + 4);

A hned se nabízí otázka, proč všude píšou typ před ukazatel? Vše je jednoduché, předáním adresy prvního prvku pole a velikosti pole říkáme: Odtud (ukazatel) vykopejte 10 děr (velikost pole), dorazíme za dvě hodiny a ti, kteří měli kopat díry zvané traktor a kopat jámu. Abychom se do této situace nedostali, bylo nutné specifikovat velikost díry, v naší analogii je typ ukazatele stejný jako typ určuje, kolik bytů bude proměnná zabírat v paměti.

Zadáním typu ukazatele tedy sdělíme kompilátoru, zde je adresa začátku pole, jeden prvek pole zabírá 2 bajty, takových prvků je v poli 10, takže kolik paměti máme alokovat toto pole? 20 bajtů - kompilátor odpoví. Pro přehlednost si vezměme ukazatel typu void, není definováno, kolik místa zabírá- toto je jen adresa, převedeme ji na ukazatele odlišné typy a proveďte operaci deaddressing.


Funkci můžete také předat ukazatel na strukturu. Vzhledem k tomu, že označení struktury je známé, stačí předat adresu jejího začátku a kompilátor ji sám rozdělí na pole.

No, poslední otázka je, kdo přišel s tímto ukazatelem. Abychom této problematice porozuměli, musíme se obrátit na assembler, například AVR, a tam najdeme návod
st X, r1 ;uložte obsah r1 do SRAM na adresu X, kde X je dvojice registrů r26, r27 ld r1,X ; načíst obsah SRAM do r1 na adrese X, kde X je dvojice registrů r26, r27
Je jasné, že X obsahuje ukazatel(adresa) a ukázalo se, že neexistuje žádný zlý člověk, který přišel s ukazatelem, který by všechny oklamal, práce s ukazateli (adresami) je podporována na úrovni jádra MK.

Ukazatele jsou extrémně Výkonný nástroj v programování. Pomocí ukazatelů lze některé věci v programování značně usnadnit a zároveňEfektivita vašeho programu se výrazně zvýší. Ukazatele dokonce umožňují zpracovávat neomezené množství dat. Například můžete použít ukazatele ke změně hodnot proměnných ve funkci, přičemž proměnné jsou předány funkci jako parametry. Kromě toho lze ukazatele použít k dynamickému přidělování paměti, což znamená, že můžete psát programy, které dokážou zpracovat prakticky neomezené množství dat za chodu – při psaní programu nemusíte vědět, kolik paměti předem alokovat. Toto je možná nejvýkonnější funkce ukazatelů. Za prvé, pojďme jen obecně porozumět ukazatelům, naučit se, jak je deklarovat a používat.

Co jsou ukazatele a proč jsou potřebné?

Ukazatele jsou jako štítky, které odkazují na místa v paměti. Představte si trezor s různě velkými schránkami ve vaší místní bance. Každá buňka má číslo, jedinečné číslo, které je spojeno pouze s touto buňkou, takže můžete rychle identifikovat požadovanou buňku. Tato čísla jsou podobná adresám paměťových buněk počítače. Máte například bohatého strýce, který si všechny cennosti schovává ve svém trezoru. A aby si zajistil všechny své úspory, rozhodl se mít menší trezor, do kterého vloží kartičku, která ukazovala umístění velkého trezoru a označovala 16. heslo k velkému trezoru, ve kterém byly uloženy skutečné šperky. Trezor s mapou v podstatě uloží polohu dalšího trezoru. Celá tato organizace šetřící šperky je ekvivalentní ukazatelům v C. V počítači jsou ukazatele jednoduše proměnné, které ukládají adresy paměti, obvykle adresy jiných proměnných.

Myšlenka je taková, že když znáte adresu proměnné, můžete na tuto adresu přejít a získat data v ní uložená. Pokud potřebujete do funkce předat obrovský kus dat, je mnohem jednodušší předat adresu paměti, kde jsou tato data uložena, než kopírovat každý kus dat! Navíc, pokud program potřebuje více paměti, můžete si vyžádat více paměti od systému. Jak to funguje? Systém jednoduše vrátí adresu místa v paměti a tuto adresu musíme uložit do proměnné ukazatele. Tímto způsobem můžeme interagovat s daty ze zadaného paměťového místa.

Syntaxe ukazatele

Pokud máme ukazatel, můžeme získat jeho adresu v paměti a data, na která odkazuje, z tohoto důvodu mají ukazatele poněkud neobvyklou syntaxi, která se liší od jednoduchých deklarací proměnných. Navíc, protože ukazatele nejsou běžné proměnné, musíte kompilátoru sdělit, že proměnná je ukazatel, a sdělit kompilátoru typ dat, na který ukazatel odkazuje. Takže ukazatel je deklarován takto:

Datový_typ *název_ukazatele;

kde datový_typ je datový typ, pointerName je název ukazatele.

Například deklarujme ukazatel, který ukládá adresu paměťové buňky obsahující celé číslo:

Int *integerPointer;

Všimněte si použití symbolu * při deklaraci ukazatele. Tento znak je klíčovým znakem v deklaraci ukazatele. Pokud tento symbol přidáte do deklarace proměnné bezprostředně před název proměnné, bude proměnná deklarována jako ukazatel. Pokud navíc deklarujete více ukazatelů na stejném řádku, musí před každým z nich předcházet hvězdička. Podívejme se na několik příkladů:

// Deklarace ukazatele a jednoduché proměnné na jednom řádku int *pointer1, // toto je ukazatelová proměnná; // toto je regulérní proměnná typu int // Deklarace dvou ukazatelů na jednom řádku int *pointer1, // toto je ukazatel s názvem pointer1 *pointer2; // toto je ukazatel s názvem pointer2

Jak jsem řekl, pokud před názvem proměnné nepředchází symbol *, pak je to běžná proměnná, jinak je to ukazatel. Přesně to ukazuje výše uvedený příklad deklarace ukazatele.

Ukazatel lze použít dvěma způsoby:

  1. Použijte název ukazatele bez symbolu *, tímto způsobem můžete získat skutečnou adresu místa v paměti, kam ukazatel odkazuje.
  2. K získání hodnoty uložené v paměti použijte název ukazatele se symbolem *. V kontextu ukazatelů má symbol * technický název: operace dereference. V podstatě bereme odkaz na nějakou adresu paměti, abychom získali skutečnou hodnotu. To může být těžké pochopit, ale v budoucnu se to všechno pokusíme pochopit.

Deklarace ukazatele, získání adresy proměnné

Abyste mohli deklarovat ukazatel, který bude odkazovat na proměnnou, musíte nejprve získat adresu této proměnné. Chcete-li získat paměťovou adresu proměnné (její umístění v paměti), musíte před názvem proměnné použít znak &. To umožňuje zjistit adresu paměťové buňky, ve které je uložena hodnota proměnné. Tato operace se nazývá operace převzetí adresy a vypadá takto:

Int var = 5; // jednoduchá deklarace proměnné s předběžnou inicializací int *ptrVar; // deklaroval ukazatel, ale zatím na nic neukazuje ptrVar = // nyní náš ukazatel odkazuje na adresu v paměti, kde je uloženo číslo 5

V řádek 3 byla použita operace převzetí adresy, vzali jsme adresu proměnné var a přiřadili ji ukazateli ptrVar. Podívejme se na program, který názorně ukáže sílu ukazatelů. Tak tady je zdroj:

#zahrnout int main() ( int var; // běžná celočíselná proměnná int *ptrVar; // celočíselný ukazatel (ptrVar musí být typu int, protože bude odkazovat na proměnnou typu int) ptrVar = // přiřadil ukazateli adresu místo v paměti, kde leží hodnota proměnné var scanf("%d", &var); // proměnná var obsahuje hodnotu zadanou z klávesnice printf("%d\n", *ptrVar); // výstup hodnota přes ukazatel getchar(); )

Výsledek programu:

V řádek 10, printf() vypíše hodnotu uloženou v proměnné var. Proč se tohle děje? No, podívejme se na kód. V řádek 5 deklarovali jsme proměnnou var typu int. V řádek 6— ukazatel ptrVar na celočíselnou hodnotu. Poté byla ukazateli ptrVar přiřazena adresa proměnné var, k tomu jsme použili operátor přiřazení adresy. Uživatel poté zadá číslo, které je uloženo v proměnné var, nezapomeňte, že je to stejné umístění, na které ukazuje ptrVar. Protože ampersand používáme k přiřazení hodnoty proměnné var in funkce scanf() , mělo by být jasné, že scanf() inicializuje proměnnou var prostřednictvím adresy. Ukazatel ptrVar ukazuje na stejnou adresu.

Pak v řádek 10, je provedena operace „dereferencování“ - *ptrVar. Program prostřednictvím ukazatele ptrVar přečte adresu, která je uložena v ukazateli, přejde do požadované paměťové buňky na adrese a vrátí hodnotu, která je tam uložena.

Všimněte si, že ve výše uvedeném příkladu se před použitím ukazatele nejprve inicializuje, aby se zajistilo, že ukazatel ukazuje na konkrétní adresu paměti. Pokud bychom začali používat ukazatel bez jeho inicializace, odkazoval by na libovolné místo v paměti. A to by mohlo vést k velmi nepříjemným následkům. Operační systém například pravděpodobně zabrání vašemu programu v přístupu k neznámému umístění v paměti, protože operační systém ví, že váš program neprovádí inicializaci ukazatele. V podstatě to jen způsobí selhání programu.

Pokud by takové triky byly v OS povoleny, mohli byste přistupovat k libovolnému umístění v paměti. A to znamená pro kohokoli běžící program můžete provést vlastní změny, například pokud máte dokument otevřený ve Wordu, můžete programově změnit libovolný text. Naštěstí Windows a další moderní OS vám zabrání v přístupu k této paměti a předčasně ukončí váš program.

Proto si pamatujte, že abyste předešli zhroucení vašeho programu, měli byste vždy inicializovat ukazatele před jejich použitím.

P.S.: Pokud na telefonu nemáte peníze a není možné je dobít, ale potřebujete naléhavě zavolat, můžete vždy použít platbu důvěry Beeline. Součet platba důvěry může být velmi rozmanitá, od 50 do 300 rublů.

I když většina programátorů chápe rozdíl mezi objekty a ukazateli na ně, někdy není zcela jasné, jaký způsob přístupu k objektu by měl být zvolen. Níže jsme se pokusili na tuto otázku odpovědět.

Otázka

Všiml jsem si, že často programátoři, jejichž kód jsem viděl, používají ukazatele na objekty častěji než tyto objekty samotné, tj. například používají následující konstrukci:

Object *myObject = nový objekt;

Objekt myObject;

To samé s metodami. Proč místo toho:

MyObject.testFunc();

měli bychom napsat toto:

MyObject->testFunc();

Pokud tomu rozumím, zvyšuje to rychlost, protože... přistupujeme přímo k paměti. Že jo? P.S. Přešel jsem z Javy.

Odpovědět

Všimněte si mimochodem, že v Javě se ukazatele nepoužívají explicitně, tzn. Programátor nemůže přistupovat k objektu v kódu přes ukazatel na něj. Ve skutečnosti jsou však v Javě všechny typy, kromě základních, referenčními typy: přistupuje se k nim odkazem, ačkoli není možné explicitně předat parametr odkazem. A také, všimněte si, nové v C++ a v Javě nebo C# jsou úplně jiné věci.

Abychom měli trochu představu o tom, jaké ukazatele jsou v C++, zde jsou dva podobné fragmenty kódu:

Objekt objekt1 = new Object(); // Nový objekt Object object2 = new Object(); // Další nový objekt objekt1 = objekt2; // Obě proměnné odkazují na objekt dříve odkazovaný objektem2 // Pokud se objekt, na který odkazuje objekt1, změní, // objekt2 se také změní, protože se jedná o stejný objekt

Nejbližší ekvivalent v C++ je:

Objekt * objekt1 = nový Objekt(); // Paměť je přidělena novému objektu // Na tuto paměť odkazuje objekt objekt1 Objekt * objekt2 = new Object(); // Totéž s druhým objektem delete object1; // C++ nemá systém pro shromažďování odpadků, takže pokud to neuděláte, // program již nebude mít přístup k této paměti, // alespoň dokud nebude program restartován // Toto se nazývá objekt úniku paměti1 = objekt2; // Stejně jako v Javě, objekt1 ukazuje na stejné místo jako objekt2

To je však úplně jiná věc (C++):

Objekt objekt1; // Nový objekt Object object2; // Jiný objekt1 = objekt2; // Úplné zkopírování objektu2 do objektu1, // spíše než předefinování ukazatele, je velmi nákladná operace

Ale získáme rychlost přímým přístupem k paměti?

Přísně vzato, tato otázka kombinuje dvě různé problémy. Za prvé: kdy byste měli použít dynamickou alokaci paměti? Za druhé: kdy byste měli používat ukazatele? Zde se samozřejmě neobejdeme bez obecných slov, že je vždy nutné zvolit pro danou práci nejvhodnější nástroj. Téměř vždy existuje lepší implementace než použití ruční dynamické alokace (dynamická alokace) a/nebo nezpracované ukazatele.

Dynamická distribuce

Znění otázky představuje dva způsoby, jak vytvořit objekt. A hlavním rozdílem je jejich životnost (doba uložení) v paměti programu. Použití Object myObject; , spoléháte na automatickou celoživotní detekci a objekt bude zničen, jakmile opustí svůj rozsah. Ale Object *myObject = new Object; udržuje objekt naživu, dokud jej ručně nevymažete z paměti příkazem delete. Poslední možnost používejte pouze v případě, že je to opravdu nutné. A proto Vždy zvolte automatické určení doby použitelnosti předmětu, pokud je to možné.

Vynucené určení doby životnosti se obvykle používá v následujících situacích:

  • Potřebujete, aby objekt existoval i po opuštění jeho rozsahu- přesně tento objekt, přesně v této oblasti paměti, a ne jeho kopie. Pokud to pro vás není důležité (ve většině případů ano), spolehněte se na automatické určení životnosti. Zde je však příklad situace, kdy možná budete potřebovat přistupovat k objektu mimo jeho rozsah, ale můžete to udělat bez jeho explicitního uložení: Zapsáním objektu do vektoru můžete „zrušit spojení“ s objektem samotným - ve skutečnosti bude (a ne jeho kopie) k dispozici při volání z vektoru.
  • Musíte použít hodně paměti, který může přetéct zásobník. Je skvělé, když se s takovým problémem nemusíte potýkat (a setkáte se s ním jen zřídka), protože je to „mimo kompetence“ C++, ale bohužel někdy musíte vyřešit i tento problém.
  • Například nevíte přesně velikost pole, které budete muset použít. Jak víte, v C++ mají pole při definování pevnou velikost. To může způsobit problémy například při čtení uživatelského vstupu. Ukazatel definuje pouze oblast v paměti, kam bude zapsán začátek pole, zhruba řečeno, bez omezení jeho velikosti.

Pokud je použití dynamické alokace nezbytné, měli byste ji zapouzdřit pomocí chytrého ukazatele (můžete si přečíst v našem článku) nebo jiného typu, který podporuje idiom „Získání zdroje se inicializuje“ (standardní kontejnery to podporují - toto je idiom podle jaký zdroj: bloková paměť, soubor, internetové připojení a tak dále. - po přijetí je inicializován v konstruktoru a poté pečlivě zničen destruktorem). Chytré ukazatele jsou například std::unique_ptr a std::shared_ptr .

Směrovky

Existují však případy, kdy je použití ukazatelů opodstatněné nejen z hlediska dynamické alokace paměti, ale téměř vždy existuje alternativní způsob, bez použití ukazatelů, který byste měli zvolit. Jako dříve, řekněme: vždy se rozhodujte pro alternativu, pokud neexistuje zvláštní potřeba použít ukazatele.

Mezi případy, kdy lze použití ukazatelů považovat za možnou možnost, patří následující:

  • Referenční sémantika. Někdy může být nutné přistupovat k objektu (bez ohledu na to, jak je pro něj alokována paměť), protože chcete přistupovat k funkcím v tomto objektu, a ne k jeho kopii – tzn. když potřebujete implementovat pass by reference. Zde však ve většině případů stačí použít odkaz a ne ukazatel, protože k tomu jsou odkazy vytvořeny. Všimněte si, že se jedná o mírně odlišné věci od toho, co bylo popsáno v bodě 1 výše. Ale pokud máte přístup ke kopii objektu, pak není nutné používat odkaz (ale pamatujte, že kopírování objektu je nákladná operace).
  • Polymorfismus. Volání funkcí v rámci polymorfismu (dynamická třída objektů) je možné pomocí odkazu nebo ukazatele. Opět je výhodné používat odkazy.
  • Nepovinný objekt. V tomto případě můžete použít nullptr k označení, že objekt je vynechán. Pokud se jedná o argument funkce, pak je lepší jej implementovat s výchozími argumenty nebo přetížením. Případně můžete použít typ, který toto chování zapouzdřuje, jako je boost::optional (upraveno v C++14 std::optional).
  • Zlepšení rychlosti kompilace. Možná budete muset oddělit jednotky kompilace (kompilační jednotky). Jedním z efektivních použití ukazatelů je předdeklarace (protože chcete-li použít objekt, musíte jej nejprve definovat). To vám umožní rozmístit kompilační jednotky, což může mít pozitivní vliv na urychlení doby kompilace a výrazně zkrátit čas strávený tímto procesem.
  • Interakce s knihovnouC nebo C-jako. Zde budete muset použít nezpracované ukazatele a uvolnit z nich paměť na poslední chvíli. Surový ukazatel můžete získat například z chytrého ukazatele pomocí operace get. Pokud knihovna používá paměť, která musí být později uvolněna ručně, můžete destruktor zarámovat do inteligentního ukazatele.