Az adatstruktúra domináns fogalomként történő alkalmazásával – a fenti példához hasonlóan – sok esetben szellemes megoldáshoz juthatunk. Ha az így tervezett programot meg kell valósítanunk, akkor a példányosítás lehetőségének korlátai a láthatósági viszonyok megváltozását is eredményezhetik.
Példaként induljunk ki a korábban definiált veremből, amelyet az alábbi műveletekkel adtunk meg.
|
Művelet |
Értelmezési tartomány |
Értékkészlet |
|
NEW |
( ) |
verem |
|
PUSH |
(verem, elem) |
verem |
|
POP |
(verem) |
verem |
|
TOP |
(verem) |
elem |
|
EMPTY |
(verem) |
boolean |
Amíg egyetlen példányunk van a veremből, addig a műveletekben a veremre való hivatkozás felesleges, hiszen a művelet nem vonatkozhat másra. Ebben az esetben a vermet implementáló adatstruktúra és a függvények egyetlen C programmodulban definiálhatók, amelyből a külvilág számára csak a verem.h-ban extern-ként megadott függvények láthatók.
VEREM.C:
static struct Verem { ... };
void New( ) { ... };
void Push(struct elem * x) { ... };
void Pop(struct elem * x) { ... };
void Top (struct elem * x) { ... };
BOOL Empty( ) { ... };
VEREM.H:
extern void New( );
extern void Push(struct elem * x);
extern void Pop(struct elem * x);
extern void Top (struct elem * x);
extern BOOL Empty( );
A vermet használó program:
#include "verem.h"
struct elem { ... } a,b,c;
...
New( );
Push(&b);
...
Top(&c);
if (Empty( )) ...
Természetesen a verem számára definiálni kell az elem struktúrát, amit célszerű a program deklarációs (header) fájljából átvenni.
Ha két vagy több vermet használunk, akkor meg kell adni, hogy melyik veremre vonatkozzon a műveletünk. Ezt két módon tehetjük meg. Az egyik megoldás szerint annyi veremmodult hozunk létre különböző nevek alatt (például a műveletek neve elé illesztett előtaggal jellemezve: APush, BPush, illetve Averem, Bverem stb.), ahányra csak szükségünk van. Ennek a megoldásnak az a hátránya, hogy a programunkban csak statikusan deklarált vermeket használhatunk, hiszen a vermek neve és száma fordítási illetve szerkesztési időben kerül meghatározásra. Dinamikusan nem hozhatunk létre vermet, hiszen ez annyit tenne, hogy a programunkhoz futási időben kellene újabb modult kapcsolni. A megoldás előnye, hogy a vermek belső szerkezete rejtve marad. Elviekben a különböző vermekhez különböző implementációs módszerek tartozhatnak. Például az Averem lehet egy tömb, míg a Bverem dinamikus szerkezet.
A másik megoldás az, hogy egyetlen vermet implementáló függvény-csoportot hozunk létre, amelynek kívülről átadandó paramétere lesz a verem:
void New(struct Verem * v) { ... };
void Push(struct Verem * v; struct elem * x) { ... };
void Pop(struct Verem * v; struct elem * x) { ... };
void Top (struct Verem * v; struct elem * x) { ... };
BOOL Empty(struct Verem * v;) { ... };
Ezen megoldás előnye, hogy a függvények egyetlen példányban jelennek meg, ugyanakkor nagy hátránya, hogy megsértjük az információk elrejtésének elvét, hiszen az adatszerkezetek az alkalmazók számára láthatóvá válnak. Nemcsak láthatók, de a felhasználók felelősségévé válik a veremtípusú változók korrekt definiálása és kezelése.
A fenti példából látható, hogy az absztrakt adatszerkezetek alkalmazásának központi problémája a példányosítás, azaz miként tudunk definiált tulajdonságokkal (műveletekkel) leírt adatszerkezetekből tetszőleges számú példányt létrehozni. Példányosítást alkalmazunk akkor, ha például megadjuk az
int a, b;
kifejezést. Ekkor keletkezik két olyan példány (a és b néven) az int adatszerkezetből, amelyeken az int-re vonatkozó műveletek végrehajthatók. Az int belső szerkezete rejtett, az egyszerű felhasználás során nem szükséges ismernünk a tárolás szabályát (például byte-sorrend, vagy 2-es komplemens módszer). A műveletek a programozási nyelvekben a szabványos típusokhoz – így az int-hez is – előre meghatározottak.
A programozási nyelvek egy részében bevezették a felhasználó által definiálható felsorolás típusú változókat. Ezen egyszerű szerkezetek konstansainak nevét adhatja meg a felhasználó. A műveletek rögzítettek és nagyjából az értékadásra, az összehasonlításra, az előző és a következő érték választására, illetve az értéknek az egészek halmazára történő leképezésére korlátozódnak.
A programozási nyelvekben a változókon értelmezett és megvalósított műveletek megnevezésének és jelölésének értelmezése is fontos tanulságokkal szolgál. A nyelvekben implementált különböző típusokon végezhető műveleteket – általában akkor, ha szemantikailag azonos értelmű – azonos néven érhetjük el. A művelet elvégzésének módját és az eredmény típusát az operandus típusa határozza meg. Gondoljunk itt arra az esetre, hogy önmagában a "+" műveleti jelből nem dönthető el, hogy hatására milyen eredmény születik, hiszen az eredményt az határozza meg, hogy a műveletet milyen típus változón alkalmaztuk. Hasonló módon értelmezhetjük például a ki- és beviteli folyamatokat elvégző függvényeket és eljárásokat, amelyeknek különféle paraméterei lehetnek és az eredményük is a paraméterek típusaitól függ.
A programozási nyelvekben ábrázolt műveletek jelölésében is számos, nehezen magyarázható korláttal találkozunk. Ilyen a műveleti jel és az operandusok elrendezésének viszonya. Hagyományosan a kétoperandusú matematikai műveletek úgy ábrázoljuk, hogy a műveleti jel az operandusok közé kerül. Ezt infix jelölésnek nevezzük. Például:
a + b; cx / 5.23; b1 or b2; t * u; egyik = másik;
A felhasználó által definiált szerkezeteken (változókon) csak részben engedélyezett az infix jelölés használata. Általában megengedett az értékadás és bizonyos esetekben a relációs operátorok (egyenlőség, kisebb, nagyobb) használata. Ugyanakkor, ha definiálunk például egy komplex szám típust, mint két valósat tartalmazó rekordot, azon nincs lehetőségünk az összegzés infix jelölésére. A komplex összegzést csak mint függvényt tudjuk definiálni, azaz a műveleti jelet (a függvény nevét) kötelezően az operandusok elé kell tenni. Ezt prefix jelölésnek nevezzük. Azaz
cmplplus(a, b)
A programnyelvek irányából közelítve célszerűnek látszik olyan szerkezetek kidolgozása, amelyek ugyanúgy példányosíthatók, mint a változók, ugyanakkor a rajtuk értelmezett műveletek is példányosodnak. Ez abban nyilvánul meg, hogy az elvégzendő műveletet nem csak a művelet jele vagy megnevezése határozza meg, hanem a művelet az adatszerkezettel együtt értelmezett.
Az említett tulajdonságokkal rendelkező szerkezeteket objektumoknak tekinthetjük.

Előző
Előző