Aspects méconnus des fonctions virtuelles en C++

Fonctions virtuelles : concepts fondamentaux

Les fonctions virtuelles permettent une liaison dynamique des appels lors de l'héritage. Chaque classe contenant des fonctions virtuelles possède une table de fonctions virtuelles (vtable) stockant les adresses des implémentations. Lorsqu'un pointeur de classe de base référence un objet dérivé, la vtable détermine quelle fonction exécuter à l'exécution.

Omission du mot-clé virtual

L'absence de virtual entraîne une liaison statique. L'exemple suivant montre ce comportement :

class Parent {
public:
    void afficher() { std::cout << "Parent\n"; }
};

class Enfant : public Parent {
public:
    void afficher() { std::cout << "Enfant\n"; }
};

int main() {
    Parent* ptr = new Enfant();
    ptr->afficher();  // Affiche "Parent"
    delete ptr;
}

C++11 introduit override pour garantir la redéfinition : si la signature ne correspond pas à une fonction virtuelle de base, une erreur de compilation survient.

Paramètres par défaut dans les fonctions virtuelles

Les paramètres par défaut sont résolus statiquement lors de la compilation, contrairement à la liaison dynamique des fonctions. Leur redéfinition est donc inefficace :

class Interface {
public:
    virtual void tracer(int valeur = 5) = 0;
};

class Impl : public Interface {
public:
    void tracer(int valeur = 7) override {
        std::cout << "Valeur : " << valeur << "\n";
    }
};

int main() {
    Interface* obj = new Impl();
    obj->tracer();  // Utilise la valeur par défaut de l'Interface (5)
    delete obj;
}

Fonctions virtuelles pures et leur implémentation

Une fonction virtuelle pure est déclarée avec = 0. Contrairement à une idée reçue, elle peut posséder une implémentation :

class Abstract {
public:
    virtual void operation() = 0;
};

void Abstract::operation() {
    std::cout << "Implémentation de base\n";
}

class Concrete : public Abstract {
public:
    void operation() override {
        Abstract::operation();  // Appel explicite
        std::cout << "Extension concrète\n";
    }
};

Destructeurs virtuels purs

Un destructeur peut être déclaré virtuel pur, mais nécesssite une implémentation :

class BaseAbstraite {
public:
    virtual ~BaseAbstraite() = 0;
};

BaseAbstraite::~BaseAbstraite() {
    std::cout << "Destruction base\n";
}

class Derivee : public BaseAbstraite {
public:
    ~Derivee() {
        std::cout << "Destruction dérivée\n";
    }
};

int main() {
    BaseAbstraite* obj = new Derivee();
    delete obj;  // Appelle ~Derivee() puis ~BaseAbstraite()
}

L'absence d'implémentation du destructeur pur provoque une erreur d'édition de liens. Les classes contenant des destructeurs purs restent abstraites.

Étiquettes: C++ virtual-functions polymorphism pure-virtual destructor

Publié le 13 juillet à 10h42