Abstraktní třídy a členové třídy. Abstraktní datové typy

Poslední aktualizace: 08/04/2018

Kromě běžných tříd má C# abstraktní třídy. Abstraktní třída je podobná běžné třídě. Může mít také proměnné, metody, konstruktory, vlastnosti. Jediná věc je, že při definování abstraktních tříd se používá klíčové slovo abstract:

Abstraktní třída Člověk ( public int Délka ( get; set; ) public double Weight ( get; set; ) )

Ale hlavní rozdíl je v tom, že nemůžeme použít konstruktor abstraktní třídy k vytvoření jejího objektu. Například takto:

Human h = new Human();

Proč jsou potřeba abstraktní třídy? Řekněme, že v našem programu pro bankovní sektor můžeme definovat dva hlavní subjekty: klienta banky a zaměstnance banky. Každý z těchto subjektů se bude lišit, například pro zaměstnance je nutné určit jeho pozici a pro klienta - částku na účtu. Klient a zaměstnanec tedy vytvoří samostatné třídy Klient a Zaměstnanec. Obě tyto entity mohou mít zároveň něco společného, ​​například jméno a příjmení nebo nějakou jinou společnou funkci. A je lepší přesunout tuto obecnou funkcionalitu do samostatné třídy, například Osoba, která popisuje osobu. To znamená, že třídy Zaměstnanec (zaměstnanec) a Klient (klient banky) budou odvozeny od třídy Osoba. A protože všechny objekty v našem systému budou představovat buď zaměstnance banky nebo klienta, nebudeme přímo vytvářet objekty z třídy Osoba. Takže má smysl to udělat abstraktně:

Abstraktní třída Osoba ( public string Name ( get; set; ) public Person( string name) ( Name = name; ) public void Display() ( Console.WriteLine(Name); ) ) class Klient: Osoba ( public int Sum ( get ; set; ) // částka účtu public Klient(název řetězce, int součet) : základ(název) ( Součet = součet; ) ) class Zaměstnanec: Osoba ( public string Pozice ( get; set; ) // pozice public Zaměstnanec( řetězec; jméno, pozice řetězce): základ(název) ( Pozice = pozice; ) )

Pak můžeme použít tyto třídy:

Klient klienta = nový klient("Tom", 500); Zaměstnanec zaměstnanec = nový zaměstnanec("Bob", "Apple"); client.Display(); zaměstnanec.Display();

Nebo ještě takhle:

Osoba klient = nový klient("Tom", 500); Osoba zaměstnanec = nový zaměstnanec("Bob", "Operátor");

Ale NEMŮŽEME vytvořit objekt Person pomocí konstruktoru třídy Person:

Osoba osoba = nová osoba("Bill");

Nicméně i přes to, že nemůžeme přímo zavolat konstruktor třídy Person k vytvoření objektu, přesto může konstruktor v abstraktních třídách hrát důležitou roli, zejména inicializovat některé proměnné a vlastnosti společné pro odvozené třídy, jako např. případ názvu vlastnosti. Přestože konstruktor třídy Osoba není ve výše uvedeném příkladu volán, odvozené třídy Klient a Zaměstnanec k němu stále mají přístup.

Abstraktní členové třídy

Kromě obvyklých vlastností a metod může mít abstraktní třída členy abstraktní třídy, které jsou definovány pomocí klíčového slova abstract a nemají žádnou funkčnost. Zejména abstraktní mohou být:

  • Vlastnosti

    Indexery

Abstraktní členové třídy by neměli mít modifikátor private. V tomto případě musí odvozená třída předefinovat a implementovat všechny abstraktní metody a vlastnosti, které jsou přítomny v základní abstraktní třídě. Při přepsání v odvozené třídě je taková metoda nebo vlastnost také deklarována s modifikátorem přepsání (jako při normálním přepsání virtuálních metod a vlastností). Je třeba také poznamenat, že pokud má třída alespoň jednu abstraktní metodu (nebo abstraktní vlastnost, indexátor, událost), pak musí být tato třída definována jako abstraktní.

Abstraktní členy, stejně jako virtuální, jsou součástí polymorfního rozhraní. Pokud ale v případě virtuálních metod říkáme, že dědící třída zdědí implementaci, pak v případě abstraktních metod se zdědí rozhraní reprezentované těmito abstraktními metodami.

Abstraktní metody

Udělejme například z metody Display ve výše uvedeném příkladu abstraktní:

Abstraktní třída Osoba ( public string Name ( get; set; ) public Person( string name) ( Name = name; ) public abstract void Display(); ) class Klient: Osoba ( public int Sum ( get; set; ) // sum na účtu public Client(string name, int sum) : base(name) ( Sum = sum; ) public override void Display() ( Console.WriteLine($"(Name) má účet na částce (Sum)") ; ) ) class Zaměstnanec: Osoba ( public string Position ( get; set; ) // position public Employee(name string, string position) : base(name) ( Position = position; ) public override void Display() ( Console.WriteLine ($" (pozice) (jméno)");

Abstraktní vlastnosti

Všimněte si použití abstraktních vlastností. Jejich definice je podobná definici vlastností auto. Například:

Abstraktní třída Osoba ( veřejný abstraktní řetězec Name ( get; set; ) ) class Client: Person ( private string name; public override string Name ( get ( return "Pan/pans. " + name; ) set ( name = value; ) ) ) třída Zaměstnanec: Osoba ( veřejný přepisovací řetězec Jméno ( get; set; ) )

Třída Person definuje abstraktní vlastnost s názvem Name. Vypadá to jako vlastnost auto, ale není to vlastnost auto. Protože tato vlastnost nesmí mít implementaci, má pouze prázdné bloky get a set. V odvozených třídách můžeme tuto vlastnost přepsat, udělat z ní plnohodnotnou vlastnost (jako ve třídě Klient), nebo ji udělat automatickou (jako ve třídě Zaměstnanec).

Vyhýbání se implementaci abstraktních členů

Odvozená třída musí implementovat všechny abstraktní členy základní třídy. Můžeme se však rozhodnout ji neimplementovat, ale v tomto případě musí být odvozená třída také definována jako abstraktní:

Abstraktní třída Osoba ( veřejný abstraktní řetězec Jméno ( get; set; ) ) abstraktní třída Správce: Osoba ( )

Příklad abstraktní třídy

Učebnicovým příkladem je soustava geometrických obrazců. Ve skutečnosti neexistuje žádný geometrický obrazec jako takový. Je tam kruh, obdélník, čtverec, ale prostě žádná postava. Kruh i obdélník však mají něco společného a jsou to postavy:

// abstraktní třída obrázku abstraktní třída Obrázek ( // abstraktní metoda pro získání obvodu public abstract float Perimeter(); // abstraktní metoda pro získání oblasti public abstract float Area(); ) // odvozená třída třídy obdélníku Obdélník: Obrázek ( public float Width ( get; set; ) public float Height ( get; set; ) public Rectangle (plovoucí šířka, plovoucí výška) ( this.Width = šířka; this.Height = výška; ) // přepíše získání perimeter public override float Perimeter() ( return Width * 2 + Height * 2; ) // override get the area public override float Area() ( return Width * Height; ) )

Dobrý den, obyvatelé Khabry!

Následující příspěvek je shrnutím mých myšlenek o povaze tříd a ADT. Tyto myšlenky jsou doplněny zajímavými citáty z knih guru vývoje softwaru

Úvod

Začněme tím, že se postupně přiblížíme k definici ADT. ADT je ​​především datový typ, což znamená následující:
přítomnost určitých dostupných operací s prvky tohoto typu;
a také údaje, proti kterým jsou tyto operace prováděny (rozsah hodnot).

Co znamená slovo „abstraktní“? V první řadě pojem „abstraktnost“ znamená zaměřit pozornost na něco důležitého a zároveň se musíme odpoutat od detailů, které jsou v tuto chvíli nedůležité. Definice abstrakce je dobře pokryta v knize Gradyho Boocha. Definice zní takto:

Abstrakce je výběr a předání souboru objektů společných vlastností, které určují jejich pojmové hranice a odlišují je od všech ostatních typů objektů.
Jinými slovy, abstrakce nám umožňuje „osvětlit“ potřebná objektová data a zároveň „zastínit“ data, která pro nás nejsou důležitá.

Co se tedy stane, když spojíte pojmy „datový typ“ a „abstrakce“ dohromady? Obdržíme datový typ, který nám poskytuje určitou sadu operací zajišťujících chování objektů tohoto datového typu a tento datový typ skryje i data, pomocí kterých je toto chování implementováno. Dostáváme se tedy ke konceptu ADT:

ADT je ​​datový typ, který před klienty skrývá svou interní implementaci.
Úžasné je, že použitím abstrakce nám ADT umožňuje nestarat se o detaily implementace na nízké úrovni, ale pracovat s podstatou skutečného světa na vysoké úrovni (Steve McConnell).

Věřím, že při vývoji ADT musíte nejprve definovat rozhraní, protože rozhraní by nemělo záviset na interní reprezentaci dat v ADT. Po definování operací, které tvoří rozhraní, se musíte zaměřit na data, která budou implementovat zadané chování ADT. Díky tomu získáme určitou datovou strukturu – mechanismus, který nám umožňuje ukládat a zpracovávat data. Krása ADT je ​​zároveň v tom, že pokud chceme změnit vnitřní reprezentaci dat, nemusíme procházet celým programem a měnit každý řádek kódu, který závisí na datech, která chceme změnit. ADT tato data zapouzdřuje, což vám umožňuje změnit činnost objektů tohoto typu, nikoli celý program.

Výhody ATD

Používání ADT má mnoho výhod (všechny popsané výhody lze nalézt v knize Steva McConnella „Perfect Code“):

  • Zapouzdření detailů implementace.
    To znamená, že jakmile zapouzdříme podrobnosti implementace ADT, poskytneme klientovi rozhraní, se kterým může komunikovat s ADT. Změnou detailů implementace se nezmění vnímání provozu ADT ze strany klientů.
  • Snížená složitost.
    Abstrahováním od detailů implementace se soustředíme na rozhraní, tedy na to, co ADT umí, spíše než na to, jak se to dělá. ADT nám navíc umožňuje pracovat s podstatou skutečného světa.
  • Omezení rozsahu použití dat.
    Pomocí ADT si můžeme být jisti, že data představující vnitřní strukturu ADT nebudou záviset na jiných částech kódu. V tomto případě je realizována „nezávislost“ ADT.
  • Vysoce informativní rozhraní.
    ADT vám umožňuje prezentovat celé rozhraní z hlediska doménových entit, což, jak vidíte, zvyšuje čitelnost a informativnost rozhraní.

Steve McConnell doporučuje reprezentovat datové typy nízké úrovně, jako je zásobník nebo seznam, jako ADT. Zeptejte se sami sebe, co tento seznam představuje. Pokud představuje seznam zaměstnanců banky, považujte ATD za seznam zaměstnanců banky.

Takže jsme přišli na to, co je ADT, a pojmenovali jsme výhody jeho použití. Nyní stojí za zmínku, že při vývoji tříd v OOP byste měli myslet především na ADT. Přitom, jak řekl Steve McConnell, neprogramujete v jazyce, ale s pomocí jazyka. To znamená, že budete programovat za hranicemi jazyka a nebudete se omezovat na myšlení v pojmech polí nebo jednoduchých datových typů. Místo toho budete uvažovat na vysoké úrovni abstrakce (například z hlediska tabulek nebo seznamů zaměstnanců). Třída není nic jiného než doplněk a způsob, jak implementovat koncept ADT. Můžeme dokonce reprezentovat třídu jako vzorec:
Třída = ADT + Dědičnost + Polymorfismus.
Proč byste tedy při vytváření tříd měli myslet na ADT? Protože nejprve se musíme rozhodnout, které operace budou tvořit rozhraní budoucí třídy, která data skryjeme a ke kterým poskytneme veřejný přístup. Musíme myslet na to, aby bylo rozhraní vysoce informativní, aby se kód snadno optimalizoval a testoval a jak můžeme poskytnout správnou abstrakci, abychom mohli přemýšlet o entitách reálného světa spíše než o podrobnostech implementace na nízké úrovni. Domnívám se, že až po definování ADT bychom měli přemýšlet o otázkách dědičnosti a polymorfismu.

Stojí za zmínku, že koncept ADT našel široké uplatnění v OOP, protože právě tento koncept doplňuje OOP a umožňuje snížit složitost programů v rychle se měnícím světě softwarových požadavků.

Tento článek jsem napsal proto, abych přitáhl pozornost vývojářů k ADT za účelem zlepšení kvality práce a vývoje softwaru.

Použité zdroje:

Steve McConnell – „Perfektní kód“;
Robert Sedgwick - "Algoritmy v Javě."

Vývoj abstraktních modelů pro data a způsobů zpracování těchto dat je kritickou součástí řešení počítačových problémů. Příklady toho vidíme jak na nízké úrovni v každodenním programování (například při použití polí a propojených seznamů, diskutovaných v), tak na vysoké úrovni při řešení aplikovaných problémů (jako při řešení problému s konektivitou pomocí doménové struktury pro vyhledávání spojení v "Úvod"). Tato přednáška pojednává o abstraktních datových typech (dále jen ADT), které umožňují vytvářet programy pomocí vysokoúrovňových abstrakcí. Abstraktní datové typy umožňují, aby byly abstraktní (konceptuální) transformace, které programy provádějí na datech, odděleny od jakékoli konkrétní reprezentace datové struktury a jakékoli konkrétní implementace algoritmu.

Všechny výpočetní systémy jsou založeny na úrovních abstrakce: určité fyzikální vlastnosti křemíku a dalších materiálů umožňují přijetí abstraktního modelu bitu, který může nabývat binárních hodnot 0-1; pak je abstraktní model stroje postaven na dynamických vlastnostech hodnot určité sady bitů; dále je na principu činnosti stroje pod řízením programu ve strojovém jazyce sestaven abstraktní model programovacího jazyka; a nakonec je zkonstruován abstraktní koncept algoritmu, implementovaný jako program v C++. Abstraktní datové typy umožňují pokračovat v tomto procesu dále a vyvíjet abstraktní mechanismy pro specifické výpočetní problémy na vyšší úrovni, než poskytuje systém C++, vyvíjet abstraktní mechanismy, které jsou orientovány na konkrétní aplikace a jsou vhodné pro řešení problémů v mnoha aplikačních doménách. a také vytvořit abstraktní mechanismy vyšší úrovně, které používají tyto základní návrhy. Abstraktní datové typy nám poskytují nekonečně rozšiřitelný soubor nástrojů pro řešení stále více nových problémů.

Na jednu stranu vás použití abstraktních konstrukcí zbaví starostí s jejich detailní realizací; na druhou stranu, kdy výkon program je důležitý, musíte znát náklady na provádění základních operací. Používáme mnoho základních abstrakcí zabudovaných do Hardware počítač a sloužící jako základ pro strojní instrukce; implementovat další abstrakce v softwaru; a používat další abstrakce poskytované dříve napsaným systémovým softwarem. Abstraktní konstrukce na vysoké úrovni jsou často odvozeny od jednodušších konstrukcí. Na všech úrovních platí stejný základní princip: najít nejdůležitější operace v programech a nejdůležitější charakteristiky dat a následně přesně definovat jak na abstraktní úrovni, tak navrhnout efektivní konkrétní mechanismy pro jejich implementaci. V této přednášce se podíváme na mnoho příkladů aplikace tohoto principu.

Vývoj nové úrovně abstrakce bude vyžadovat (1) definování abstraktních objektů, s nimiž je třeba manipulovat, a operací, které se s nimi musí provádět; (2) reprezentovat data v nějaké datové struktuře a provádět operace; (3) a (co je nejdůležitější) zajistit, aby tyto objekty byly vhodné pro řešení aplikovaných problémů. Tyto body platí i pro jednoduché datové typy, takže základní mechanismy pro podporu datových typů, které byly probrány v "Elementárních datových strukturách" mohou být přizpůsobeny pro naše účely. C++ však nabízí důležité rozšíření mechanismu struktury zvaného class. Třídy jsou extrémně užitečné při vytváření vrstev abstrakce, a jsou proto považovány za primární nástroj používaný pro tento účel ve zbytku knihy.

Definice 4.1. Abstraktní datový typ (ADT) je datový typ (soubor hodnot a soubor operací s těmito hodnotami), ke kterému lze přistupovat pouze prostřednictvím rozhraní. Program, který používá ADT, se bude nazývat klient a program obsahující specifikaci tohoto datového typu se bude nazývat implementace.

Je to slovo, které pouze činí datový typ abstraktním: v případě ADT nemají klientské programy přístup k datovým hodnotám jiným způsobem než operacemi popsanými v rozhraní. Reprezentace těchto dat a funkce, které tyto operace implementují, jsou v implementaci a jsou rozhraním zcela odděleny od klienta. Říkáme, že rozhraní je neprůhledné: klient nevidí implementaci přes rozhraní.

V programech C++ je toto rozlišení obvykle provedeno trochu jasněji, protože je nejjednodušší vytvořit rozhraní zahrnutím prezentace dat, ale specifikuje, že klientské programy nemají povolen přímý přístup k datům. Jinými slovy, vývojáři klientských programů mohou vědět prezentace dat, ale nelze jej žádným způsobem použít.

Jako příklad uvažujme rozhraní datového typu pro body (program 3.3) z části 3.1 „Základní datové struktury“. Toto rozhraní výslovně deklaruje, že body jsou reprezentovány jako struktury skládající se z dvojice čísel s plovoucí desetinnou čárkou, označených x a y. Tento druh použití datových typů je běžný ve velkých softwarových systémech: vyvíjíme sadu konvencí pro reprezentaci dat (a také definujeme sadu operací s nimi spojených) a zpřístupňujeme tato pravidla prostřednictvím rozhraní, aby je mohli používat klientské programy, které jsou součástí velkého systému. Datový typ zajišťuje, že všechny části systému jsou konzistentní v reprezentaci základních datových struktur celého systému. Bez ohledu na to, jak dobrá je tato strategie, má jednu chybu: pokud potřebujete změnu prezentace dat, pak budete muset změnit všechny klientské programy. Program 3.3 nám opět uvádí jednoduchý příklad: Jedním z důvodů návrhu tohoto datového typu je usnadnit klientským programům práci s body a očekáváme, že klienti budou mít v případě potřeby přístup k jednotlivým souřadnicím bodů. Nemůžeme však přejít na jinou reprezentaci dat (řekněme polární souřadnice nebo 3D souřadnice nebo dokonce různé datové typy pro jednotlivé souřadnice), aniž bychom změnili všechny klientské programy.

Na rozdíl od toho Program 4.1 obsahuje implementaci abstraktního datového typu odpovídajícího datovému typu v Programu 3.3, ale využívající třídu jazyka C++, která okamžitě definuje jak data, tak související operace. Program 4.2 je klientský program, který pracuje s tímto datovým typem. Tyto dva programy provádějí stejné výpočty jako programy 3.3 a 3.8. Ilustrují řadu základních vlastností tříd, na které se nyní podíváme.

Když do programu zapíšeme definici jako int i, říkáme systému, aby vyhradil oblast paměti pro data (vestavěného) typu int, ke kterým lze přistupovat pod jménem i. V jazyce C++ existuje pro takové entity termín jako objekt. Když program zapíše definici, jako je POINT p, říká se, že vytváří objekt třídy POINT, na který lze odkazovat jménem p. V našem příkladu každý objekt obsahuje dva datové prvky pojmenované x a y. Stejně jako u struktur mohou být označovány jmény jako p.y.

Datové členy x a y se nazývají datové členy třídy. Třída může také definovat členské funkce, které implementují operace spojené s tímto datovým typem. Například třída definovaná v Programu 4.1 má dvě členské funkce nazvané POINT a distance.

Klientské programy, jako je Program 4.2, mohou volat členské funkce spojené s objektem zadáním jejich jmen stejným způsobem jako jména dat obsažených ve struktuře. Například výraz p.distance(q) vypočítá vzdálenost mezi body p a q (stejná vzdálenost by měla být vrácena voláním q.distance(p) ). Funkce POINT(), první funkce v Programu 4.1, je speciální členská funkce zvaná konstruktor: má stejný název jako třída a je volána, když chcete vytvořit objekt této třídy.

Program 4.1. Implementace třídy POINT

Tato třída definuje datový typ, který se skládá ze sady hodnot, které jsou „páry s plovoucí desetinnou čárkou“ (předpokládá se, že budou interpretovány jako body v kartézské rovině), a také ze dvou členských funkcí definovaných pro všechny instance třídy POINT. : funkce POINT() , což je konstruktor, který inicializuje souřadnice s náhodnými hodnotami mezi 0 a 1, a funkce distance(POINT), která vypočítá vzdálenost k jinému bodu. Prezentace dat je soukromý a lze k němu přistupovat nebo jej upravovat pouze členské funkce. Samotné členské funkce jsou veřejné a přístupné každému klientovi. Kód lze uložit například do souboru s názvem POINT.cxx.

#zahrnout class POINT ( private: float x, y; public: POINT() ( x = 1,0*rand()/RAND_MAX; y = 1,0*rand()/RAND_MAX; ) float distance(POINT a) ( float dx = x-a.x , dy = y-a.y return sqrt(dx*dx + dy*dy);

Program 4.2. Klientský program pro třídu POINT (nalezení nejbližšího bodu)

Tato verze programu 3.8 je klient, který používá POINT ADT definovaný v programu 4.3. Nová operace vytvoří pole objektů POINT (voláním konstruktoru POINT() pro inicializaci každého objektu s náhodnými hodnotami souřadnic). Výraz a[i].distance(a[j]) volá členskou funkci vzdálenosti na objektu a[i] s argumentem a[j] .

#zahrnout #zahrnout #include "BOD.cxx" int main(int argc, char *argv) ( float d = atof(argv); int i, cnt = 0, N = atoi(argv); BOD *a = nový BOD[N]; pro (i = 0; i< N; i++) for (int j = i+1; j < N; j++) if (a[i].distance(a[j]) < d) cnt+ + ; cout << cnt << " пар в радиусе " << d << endl; }

Definováním BOD p v klientském programu se alokuje oblast paměti pro nový objekt a pak (pomocí funkce POINT()) přiřadí každému z jeho dvou datových prvků náhodnou hodnotu v rozsahu od 0 do 1.

Tento styl programování, někdy nazývaný objektově orientované programování, je plně podporován konstrukcí třídy C++. Třídu lze považovat za rozšíření konceptu struktury, kde se nejen kombinují data, ale definují se i operace s těmito daty. Může existovat mnoho různých objektů patřících do stejné třídy, ale všechny jsou podobné v tom, že jejich datové členy mohou nabývat stejné sady hodnot a s těmito datovými členy lze provádět stejnou sadu operací – obecně se jedná o instance stejného datového typu. V objektově orientovaném programování jsou objekty navrženy tak, aby zpracovávaly svá členská data (na rozdíl od použití nezávislých funkcí ke zpracování dat uložených v objektech).

Podíváme se na výše popsaný příklad malé třídy, abychom se seznámili se základními funkcemi tříd; tak to zdaleka není kompletní. V reálném kódu budeme mít pro třídu bodů mnohem více operací. Například v programu 4.1 nejsou ani operace, které by vám umožnily zjistit hodnoty souřadnic x a y. Jak uvidíme, přidání těchto a dalších operací je poměrně jednoduchý úkol. V části 5 se blíže podíváme na třídy pro bod a další geometrické abstrakce, jako jsou čáry a mnohoúhelníky.

V C++ (ale ne C) mohou mít struktury s nimi spojené funkce. Klíčový rozdíl mezi třídami a strukturami se týká přístupu k informacím, který je charakterizován klíčovými slovy

Jazyk C++ umožňuje vytvářet datové typy, které se chovají podobně jako základní typy jazyka C. Tyto typy se obvykle nazývají abstraktní datové typy(ATD).
Pro implementaci ADT v jazyce C se používají struktury. Ale použití strukturovaných datových typů je výrazně omezené ve srovnání s použitím základních datových typů. Například data struktury nelze použít jako operandy v různých operacích (sčítání, odčítání). Chcete-li manipulovat s takovými daty, musíte napsat sadu funkcí, které provádějí různé akce, a volat tyto funkce místo operací.

Prvky konstrukce navíc nejsou v žádném případě chráněny před náhodnou úpravou. To znamená, že k prvku struktury může přistupovat jakákoli funkce (i ne ze sady nástrojů pro manipulaci s daty struktury). To odporuje jednomu ze základních principů objektově orientovaného programování – zapouzdření dat: k datovým prvkům by neměly mít přístup žádné jiné funkce, kromě speciálních funkcí pro manipulaci s tímto datovým typem.

Podívejme se na implementaci konceptu data pomocí struct k definování reprezentace data a sady funkcí pro práci s proměnnými tohoto typu:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

#zahrnout
datum struktury
{
int měsíc; // Měsíc
int den; // den
int rok; // rok
};
void set_date(datum* f, int d, int m, int y)
{
f->den = d;
f->měsíc = m;
f->rok = y;
}
void print_date(date* f)
{
printf("%d.%d.%d" , f->den, f->měsíc, f->rok);
}
int main()
{
datum dnes;
set_date(&dnes, 2., 4., 2014);
print_date(&dnes);
getchar();
návrat 0;
}


Výsledek provedení

V tomto příkladu neexistuje žádné explicitní spojení mezi funkcemi a datovým typem. Chcete-li volat kteroukoli z popsaných funkcí, musíte předat ukazatel na instanci struktury jako argument.

Toto spojení lze vytvořit popisem funkcí jako členů struktury. Tyto funkce mohou působit na data obsažená v samotné struktuře.
Ve výchozím nastavení, když je struktura deklarována, jsou její data a funkce sdíleny, to znamená, že objekty typu struktury nemají ani zapouzdření, ani ochranu dat:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

#zahrnout
datum struktury
{
int měsíc; // Měsíc
int den; // den
int rok; // rok
void set_date(int d, int m, int y)
{
den = d; měsíc = m; rok = y;
}
void print_date(void);
};
void date::print_date(void)
{
printf("%d.%d.%d" , den, měsíc, rok);
}
int main()
{
datum dnes;
dnes.set_date(2, 4, 2014);
dnes.print_date();
getchar();
návrat 0;
}

Členské funkce a datové členy

Funkce deklarované v těle abstraktního datového typu jsou členské funkce nebo metody a lze je volat pouze na speciální proměnné odpovídajícího typu pomocí standardní syntaxe pro přístup k datovým členům nebo polím struktury.

Členské funkce lze definovat dvěma způsoby:

  • popis funkce přímo při popisu struktury;
  • popis funkce mimo strukturu.

Členské funkce, které jsou definovány uvnitř struktury, jsou implicitně vloženy ( ). Ve struktuře by měly být zpravidla definovány pouze krátké, často používané členské funkce:

void set(int m, int d, int y) (měsíc = m; den = d; rok = y;);



Protože různé struktury mohou mít členské funkce se stejným názvem, při definování členské funkce musíte zadat název struktury a propojit je pomocí operátoru rozlišení kontextu (dvojtečka):
Typ ADT::name(seznam argumentů) (
orgán členské funkce; )

  • typ— návratový typ členské funkce
  • ATD— název abstraktního datového typu (název struktury nebo třídy)
  • název— název členské funkce

void date::print_date(void)
( printf("%d.%d.%d" ,den, měsíc, rok);)

V členské funkci lze názvy členů použít bez explicitního odkazu na objekt. V tomto případě název odkazuje na člen objektu, na kterém byla funkce volána.

Členské funkce stejné struktury mohou být polymorfní, tedy přetížené.

Přístupová práva

Koncept struktury v C++ (na rozdíl od C) umožňuje členům struktury, aby byly veřejné, soukromé nebo chráněné:

  • veřejnost – obecná;
  • soukromý – soukromý;
  • chráněný – chráněný.

Použití chráněného klíčového slova souvisí s konceptem dědičnosti.

Použití klíčového slova private omezuje přístup členům, kteří se řídí touto konstrukcí. Soukromé členy může používat pouze několik kategorií funkcí, jejichž oprávnění zahrnují přístup k těmto členům. V zásadě se jedná o členské funkce stejné struktury.
Klíčové slovo public tvoří rozhraní k objektu struktury.

Standardně se členská data umísťují do soukromé oblasti (private) a část členských funkcí do veřejné části (veřejné) abstraktního datového typu. V tomto případě privátní část definuje objektová data a servisní funkce a členské funkce veřejné části implementují metody pro práci s objektem.

Změňme strukturu data, abychom skryli reprezentaci dat (zapouzdření dat):

1
2
3
4
5
6
7
8

datum struktury
{
soukromé:
int měsíc, den, rok;
veřejnost:
void set(int, int, int);
void print();
};

Vývoj abstraktních modelů pro data a způsobů zpracování těchto dat je kritickou součástí řešení počítačových problémů. Příklady toho vidíme jak na nízké úrovni v každodenním programování (například při použití polí a propojených seznamů, diskutovaných v), tak na vysoké úrovni při řešení aplikovaných problémů (jako při řešení problému s konektivitou pomocí doménové struktury pro vyhledávání spojení v "Úvod"). Tato přednáška pojednává o abstraktních datových typech (dále jen ADT), které umožňují vytvářet programy pomocí vysokoúrovňových abstrakcí. Abstraktní datové typy umožňují, aby byly abstraktní (konceptuální) transformace, které programy provádějí na datech, odděleny od jakékoli konkrétní reprezentace datové struktury a jakékoli konkrétní implementace algoritmu.

Všechny výpočetní systémy jsou založeny na úrovních abstrakce: určité fyzikální vlastnosti křemíku a dalších materiálů umožňují přijetí abstraktního modelu bitu, který může nabývat binárních hodnot 0-1; pak je abstraktní model stroje postaven na dynamických vlastnostech hodnot určité sady bitů; dále je na principu činnosti stroje pod řízením programu ve strojovém jazyce sestaven abstraktní model programovacího jazyka; a nakonec je zkonstruován abstraktní koncept algoritmu, implementovaný jako program v C++. Abstraktní datové typy umožňují pokračovat v tomto procesu dále a vyvíjet abstraktní mechanismy pro specifické výpočetní problémy na vyšší úrovni, než poskytuje systém C++, vyvíjet abstraktní mechanismy, které jsou orientovány na konkrétní aplikace a jsou vhodné pro řešení problémů v mnoha aplikačních doménách. a také vytvořit abstraktní mechanismy vyšší úrovně, které používají tyto základní návrhy. Abstraktní datové typy nám poskytují nekonečně rozšiřitelný soubor nástrojů pro řešení stále více nových problémů.

Na jednu stranu vás použití abstraktních konstrukcí zbaví starostí s jejich detailní realizací; na druhou stranu, kdy výkon program je důležitý, musíte znát náklady na provádění základních operací. Používáme mnoho základních abstrakcí zabudovaných do Hardware počítač a sloužící jako základ pro strojní instrukce; implementovat další abstrakce v softwaru; a používat další abstrakce poskytované dříve napsaným systémovým softwarem. Abstraktní konstrukce na vysoké úrovni jsou často odvozeny od jednodušších konstrukcí. Na všech úrovních platí stejný základní princip: najít nejdůležitější operace v programech a nejdůležitější charakteristiky dat a následně přesně definovat jak na abstraktní úrovni, tak navrhnout efektivní konkrétní mechanismy pro jejich implementaci. V této přednášce se podíváme na mnoho příkladů aplikace tohoto principu.

Vývoj nové úrovně abstrakce bude vyžadovat (1) definování abstraktních objektů, s nimiž je třeba manipulovat, a operací, které se s nimi musí provádět; (2) reprezentovat data v nějaké datové struktuře a provádět operace; (3) a (co je nejdůležitější) zajistit, aby tyto objekty byly vhodné pro řešení aplikovaných problémů. Tyto body platí i pro jednoduché datové typy, takže základní mechanismy pro podporu datových typů, které byly probrány v "Elementárních datových strukturách" mohou být přizpůsobeny pro naše účely. C++ však nabízí důležité rozšíření mechanismu struktury zvaného class. Třídy jsou extrémně užitečné při vytváření vrstev abstrakce, a jsou proto považovány za primární nástroj používaný pro tento účel ve zbytku knihy.

Definice 4.1. Abstraktní datový typ (ADT) je datový typ (soubor hodnot a soubor operací s těmito hodnotami), ke kterému lze přistupovat pouze prostřednictvím rozhraní. Program, který používá ADT, se bude nazývat klient a program obsahující specifikaci tohoto datového typu se bude nazývat implementace.

Je to slovo, které pouze činí datový typ abstraktním: v případě ADT nemají klientské programy přístup k datovým hodnotám jiným způsobem než operacemi popsanými v rozhraní. Reprezentace těchto dat a funkce, které tyto operace implementují, jsou v implementaci a jsou rozhraním zcela odděleny od klienta. Říkáme, že rozhraní je neprůhledné: klient nevidí implementaci přes rozhraní.

V programech C++ je toto rozlišení obvykle provedeno trochu jasněji, protože je nejjednodušší vytvořit rozhraní zahrnutím prezentace dat, ale specifikuje, že klientské programy nemají povolen přímý přístup k datům. Jinými slovy, vývojáři klientských programů mohou vědět prezentace dat, ale nelze jej žádným způsobem použít.

Jako příklad uvažujme rozhraní datového typu pro body (program 3.3) z části 3.1 „Základní datové struktury“. Toto rozhraní výslovně deklaruje, že body jsou reprezentovány jako struktury skládající se z dvojice čísel s plovoucí desetinnou čárkou, označených x a y. Tento druh použití datových typů je běžný ve velkých softwarových systémech: vyvíjíme sadu konvencí pro reprezentaci dat (a také definujeme sadu operací s nimi spojených) a zpřístupňujeme tato pravidla prostřednictvím rozhraní, aby je mohli používat klientské programy, které jsou součástí velkého systému. Datový typ zajišťuje, že všechny části systému jsou konzistentní v reprezentaci základních datových struktur celého systému. Bez ohledu na to, jak dobrá je tato strategie, má jednu chybu: pokud potřebujete změnu prezentace dat, pak budete muset změnit všechny klientské programy. Program 3.3 nám opět uvádí jednoduchý příklad: Jedním z důvodů návrhu tohoto datového typu je usnadnit klientským programům práci s body a očekáváme, že klienti budou mít v případě potřeby přístup k jednotlivým souřadnicím bodů. Nemůžeme však přejít na jinou reprezentaci dat (řekněme polární souřadnice nebo 3D souřadnice nebo dokonce různé datové typy pro jednotlivé souřadnice), aniž bychom změnili všechny klientské programy.

Na rozdíl od toho Program 4.1 obsahuje implementaci abstraktního datového typu odpovídajícího datovému typu v Programu 3.3, ale využívající třídu jazyka C++, která okamžitě definuje jak data, tak související operace. Program 4.2 je klientský program, který pracuje s tímto datovým typem. Tyto dva programy provádějí stejné výpočty jako programy 3.3 a 3.8. Ilustrují řadu základních vlastností tříd, na které se nyní podíváme.

Když do programu zapíšeme definici jako int i, říkáme systému, aby vyhradil oblast paměti pro data (vestavěného) typu int, ke kterým lze přistupovat pod jménem i. V jazyce C++ existuje pro takové entity termín jako objekt. Když program zapíše definici, jako je POINT p, říká se, že vytváří objekt třídy POINT, na který lze odkazovat jménem p. V našem příkladu každý objekt obsahuje dva datové prvky pojmenované x a y. Stejně jako u struktur mohou být označovány jmény jako p.y.

Datové členy x a y se nazývají datové členy třídy. Třída může také definovat členské funkce, které implementují operace spojené s tímto datovým typem. Například třída definovaná v Programu 4.1 má dvě členské funkce nazvané POINT a distance.

Klientské programy, jako je Program 4.2, mohou volat členské funkce spojené s objektem zadáním jejich jmen stejným způsobem jako jména dat obsažených ve struktuře. Například výraz p.distance(q) vypočítá vzdálenost mezi body p a q (stejná vzdálenost by měla být vrácena voláním q.distance(p) ). Funkce POINT(), první funkce v Programu 4.1, je speciální členská funkce zvaná konstruktor: má stejný název jako třída a je volána, když chcete vytvořit objekt této třídy.

Program 4.1. Implementace třídy POINT

Tato třída definuje datový typ, který se skládá ze sady hodnot, které jsou „páry s plovoucí desetinnou čárkou“ (předpokládá se, že budou interpretovány jako body v kartézské rovině), a také ze dvou členských funkcí definovaných pro všechny instance třídy POINT. : funkce POINT() , což je konstruktor, který inicializuje souřadnice s náhodnými hodnotami mezi 0 a 1, a funkce distance(POINT), která vypočítá vzdálenost k jinému bodu. Prezentace dat je soukromý a lze k němu přistupovat nebo jej upravovat pouze členské funkce. Samotné členské funkce jsou veřejné a přístupné každému klientovi. Kód lze uložit například do souboru s názvem POINT.cxx.

#zahrnout class POINT ( private: float x, y; public: POINT() ( x = 1,0*rand()/RAND_MAX; y = 1,0*rand()/RAND_MAX; ) float distance(POINT a) ( float dx = x-a.x , dy = y-a.y return sqrt(dx*dx + dy*dy);

Program 4.2. Klientský program pro třídu POINT (nalezení nejbližšího bodu)

Tato verze programu 3.8 je klient, který používá POINT ADT definovaný v programu 4.3. Nová operace vytvoří pole objektů POINT (voláním konstruktoru POINT() pro inicializaci každého objektu s náhodnými hodnotami souřadnic). Výraz a[i].distance(a[j]) volá členskou funkci vzdálenosti na objektu a[i] s argumentem a[j] .

#zahrnout #zahrnout #include "BOD.cxx" int main(int argc, char *argv) ( float d = atof(argv); int i, cnt = 0, N = atoi(argv); BOD *a = nový BOD[N]; pro (i = 0; i< N; i++) for (int j = i+1; j < N; j++) if (a[i].distance(a[j]) < d) cnt+ + ; cout << cnt << " пар в радиусе " << d << endl; }

Definováním BOD p v klientském programu se alokuje oblast paměti pro nový objekt a pak (pomocí funkce POINT()) přiřadí každému z jeho dvou datových prvků náhodnou hodnotu v rozsahu od 0 do 1.

Tento styl programování, někdy nazývaný objektově orientované programování, je plně podporován konstrukcí třídy C++. Třídu lze považovat za rozšíření konceptu struktury, kde se nejen kombinují data, ale definují se i operace s těmito daty. Může existovat mnoho různých objektů patřících do stejné třídy, ale všechny jsou podobné v tom, že jejich datové členy mohou nabývat stejné sady hodnot a s těmito datovými členy lze provádět stejnou sadu operací – obecně se jedná o instance stejného datového typu. V objektově orientovaném programování jsou objekty navrženy tak, aby zpracovávaly svá členská data (na rozdíl od použití nezávislých funkcí ke zpracování dat uložených v objektech).

Podíváme se na výše popsaný příklad malé třídy, abychom se seznámili se základními funkcemi tříd; tak to zdaleka není kompletní. V reálném kódu budeme mít pro třídu bodů mnohem více operací. Například v programu 4.1 nejsou ani operace, které by vám umožnily zjistit hodnoty souřadnic x a y. Jak uvidíme, přidání těchto a dalších operací je poměrně jednoduchý úkol. V části 5 se blíže podíváme na třídy pro bod a další geometrické abstrakce, jako jsou čáry a mnohoúhelníky.

V C++ (ale ne C) mohou mít struktury s nimi spojené funkce. Klíčový rozdíl mezi třídami a strukturami se týká přístupu k informacím, který je charakterizován klíčovými slovy




Horní