Virtuálne vs nevirtuálne metódy rubrika: Programování: C/C++

7 xxar3s
položil/-a 24.12.2020
 
upravil/-a 24.12.2020

Momentálne v práci programujem najviac v jazykoch, ktoré majú všetky metódy implicitne virtuálne (JS, Typescript).

Ale vo volnom čase používam jazyky, kde máme možnosť výberu medzi virtálnou a nevirtuálnou metódou (F#, C++, C#) v týchto jazykoch, ktoré sa orientujú na rýchlosť sú metódy implicitne ne-virtuálne a keď chceme virtuálnu metódu, treba ju označiť slovom virtual (v F# inak ale to tu nechcem rozoberať). Naopak v Jave sú metódy implicitne virtuálne (okrem privátnych) a keď chceme nevirtuálnu metódu, treba ju označiť ako final.

Virtuálne metódy sú tie pri ktorých dedičnosť a polymorfizmus fungujú tak ako očakávame od objektového jazyka. Inak povedané zatiaľ čo nevirtuálne metódy sa v predkovi len overloadujú (prekrývajú) virtuálne sa overridujú (prepisujú). Pre tých čo nechápu ešte príklad:

struct Parent
{
    virtual void virtualMethod() const
    {
        std::cout << "called Parent::virtualMethod" << std::endl;
    }
 
    void nonVirtualMethod() const
    {
        std::cout << "called Parent::nonVirtualMethod" << std::endl;
    }
 
    void callVirtualMethod() const
    {
        virtualMethod();
    }
 
    void callNonVirtualMethod() const
    {
        nonVirtualMethod();
    }
};
 
struct Child : Parent
{
    void virtualMethod() const override
    {
        std::cout << "called Child::virtualMethod" << std::endl;
    }
 
    void nonVirtualMethod() const
    {
        std::cout << "called Child::nonVirtualMethod" << std::endl;
    }
};
 
void main()
{
    auto child = Child();
 
    child.callVirtualMethod(); // metoda bola prepisana metodou z potomka takze sa vypise: called Child::virtualMethod
    child.virtualMethod(); // rovnaky vysledok bude pri priamom volani: called Child::virtualMethod
 
    child.callNonVirtualMethod(); // metoda nebola prepisana, ale len prekryta takze sa zavola metoda z predka: called Parent::nonVirtualMethod
    child.nonVirtualMethod(); // ked ju ale zavolam priamo tak sa vypise: prekryta varianta: called Child::nonVirtualMethod
}

Ide o dilemu výkon vs rozšíriteľnosť. Podľa akého kľúča sa rozhodujete či definujete metódu ako virtuálnu? Alebo definujete všetky metódy ako virtuálne a nevirtuálne používate iba vy výnimočných prípadoch? (kritické miesta kde sa šetrí každá milisekunda?). Neni používanie nevirtuálnych metód predčasná optimalizácia?

Komentáře

  • David Macek : Pokud myslíte na to, aby uživatelé mohli vaši třídu využívat předem neznámými způsoby, doporučuju se kromě dědičnosti zamyslet nad kompozicí. 10.1.2021
odkaz
4 vit.herman
odpověděl/-a 30.12.2020
 
upravil/-a 31.12.2020

Metodu nastavím jako virtuální, pokud tento účel jasně vyplývá z návrhu. Rozhodně tedy ne, že jen tak pro jistotu udělám vše virtuální. Prostě musí existovat důvod proč něco dělám. Implicitní volbu za mne udělal tvůrce jazyka. Z tohoto důvodu také reálné dilema výkon vs. rozšiřitelnost nejspíš nikdy nenastane. Mohlo by snad jen u nějaké klíčové metody s extrémně častým voláním v nějaké výkonově kritické sekci.

DOPLNĚNÍ: Nepřidání klíčového slova "virtual" není optimalizací. Naopak právě jeho přidání všude by bylo předčasnou optimalizací. I když ne na výkon, ale na rozšiřitelnost.

Komentáře

  • xxar3s : Máte nejaké jednoduché pravidlo (alebo sadu pravidiel) podľa ktorého sa viete jednoznačne rozhodnúť či metódu označíte za virtuálnu? Alebo sa rozhodujte len intuitívne? 9.1.2021
  • vit.herman : Ano. Musím mít konkrétní důvod a předpoklad, že danou metodu skutečně budu já nebo uživatel knihovny rozšiřovat. Pokud udělám třeba setter setA(int a), chci, aby nastavil interní atribut "a". A nechci dovolit, aby metodu někdo overridoval a tak mohl (byť omylem) zamezit její základní funkci. A někdy už součástí myšlenky (modelu) je, že nějaká metoda je přímo stvořena pro override. Tu mohu udělat abstraktní nebo pouze virtuální s nějakou implicitní implementací. Ale opět se vracím k tomu, že o důvodech rozhoduje vývojář a samozřejmě záleží na jeho intuici, umění a zkušenostech, zda vznikne pěkný návrh nebo paskvil. Bohužel tuto realitu nezle obejít univerzálnějším pravidlem :-) Mohu s naprostou jistotou poradit jen to, abychom netvořili příliš zjednodušující pravidla typu "uděláme vše virtuální, aby bylo vše rozšiřitelné". V některých jazycích je sice virtual jako default, v některých případech je to ústupek. A nakonec třeba v Javě to považuji za ne zcela ideální návrhový koncept, který byl překonán. 9.1.2021

Pro plný přístup na Devel.cz se prosím přihlaste:

Rychlé přihlášení přes sociální sítě:

Nebo se přihlaste jménem a heslem:

Zadejte prosím svou e-mailovou adresu.
Zadejte své heslo.