46 votes

Héritage C ++ et pointeurs de fonctions membres

En C++, peut-membre des pointeurs de fonction permettant de pointer vers des dérivés (ou même de base) les membres de la classe?

EDIT: Peut-être un exemple de l'aide. Supposons que nous avons une hiérarchie de trois classes X, Y, Z dans l'ordre de succession. Y a donc une classe de base X et une classe dérivée Z.

Maintenant, nous pouvons définir une fonction membre de pointeur p pour la classe Y. C'est écrit comme:

void (Y::*p)();

(Pour plus de simplicité, je vais supposer que nous sommes seulement intéressés par les fonctions de la signature void f() )

Ce pointeur p peut maintenant être utilisé pour pointer vers des fonctions membres de la classe Y.

Cette question (deux questions) est alors:

  1. Peut - p être utilisé pour pointer vers une fonction dans la classe dérivée Z?
  2. Peut - p être utilisé pour pointer vers une fonction dans la classe de base X?

28voto

outis Points 39377

C++03 std, §4.11 2 Pointeur sur membre des conversions:

Une rvalue de type "pointeur sur membre B de type cv T", où B est un type de classe, peuvent être convertis en une rvalue de type "pointeur sur membre des D de type cv T, où D est une classe dérivée (clause 10) de B. Si B est un inaccessible (clause 11), ambigu (10.2) ou virtuel (10.1) de la classe de base de D, un programme qui nécessite cette conversion est mal formé. Le résultat de la conversion désigne le même membre que le pointeur de membre avant la conversion a eu lieu, mais il se réfère à la classe de base militaire, comme s'il s'agissait d'un membre de la classe dérivée. Le résultat se rapporte au membre D instance de B. Puisque le résultat est de type "pointeur sur membre des D de type cv T", il peut être déréférencé avec un D objet. Le résultat est le même que si le pointeur de membre de B ont été déréférencés avec le B de sous-objet de D. Le membre null la valeur du pointeur est converti à l'null membre de la valeur du pointeur du type de destination. 52)

52)La règle de conversion de pointeurs sur des membres (à partir du pointeur de membre de base de pointeur de membre de la dérivée) apparaît inversé par rapport à la règle des pointeurs vers les objets (à partir de pointeur de dérivés de pointeur de base) (4.10, article 10). Cette inversion est nécessaire pour assurer la sécurité de type. Note qu'un pointeur de membre n'est pas un pointeur vers un objet ou un pointeur de fonction et les règles pour les conversions de ces pointeurs ne s'appliquent pas à des pointeurs vers les membres. En particulier, un pointeur de membre ne peut être converti à un void*.

En bref, vous pouvez convertir un pointeur sur un membre de l'accessibilité, de non-virtuel de la classe de base à un pointeur sur un membre d'une classe dérivée tant que le membre n'est pas ambigu.

class A {
public: 
    void foo();
};
class B : public A {};
class C {
public:
    void bar();
};
class D {
public:
    void baz();
};
class E : public A, public B, private C, public virtual D {
public: 
    typedef void (E::*member)();
};
class F:public E {
public:
    void bam();
};
...
int main() {
   E::member mbr;
   mbr = &A::foo; // invalid: ambiguous; E's A or B's A?
   mbr = &C::bar; // invalid: C is private 
   mbr = &D::baz; // invalid: D is virtual
   mbr = &F::bam; // invalid: conversion isn't defined by the standard
   ...

La Conversion dans l'autre sens (par static_cast) est régie par les § 5.2.9 9:

Une rvalue de type "pointeur sur membre des D de type cv1 T" peut être convertie en une valeur r de type "pointeur sur membre B de type cv2 T", où B est une classe de base (article 10 de la classe.dérivée) de D, si une conversion standard de "pointeur sur membre B de type T" à "pointeur de membre de D de type T" existe (4.11 conv.mem), et cv2 est le même cv-qualification, ou plus cv-qualification que, cv1.11) Le membre null la valeur du pointeur (4.11 conv.mem) est converti à l'null membre de la valeur du pointeur du type de destination. Si la classe B contient le membre d'origine, ou une base ou d'une classe dérivée de la classe contenant l'origine, le pointeur résultant pour les points des membres à l'origine. Sinon, le résultat de la fonte n'est pas défini. [Remarque: bien que la classe B n'a pas besoin de contenir de l'origine, le type dynamique de l'objet sur lequel le pointeur de membre est déréférencé doit contenir le membre d'origine; voir 5.5 expr.mptr.oper.]

11) des types de Fonction (y compris ceux utilisés dans le pointeur de fonction membre les types) ne sont jamais de cv qualifiés; voir 8.3.5 dcl.fct.

En bref, vous pouvez convertir à partir d'un dérivé D::* de base - B::* si vous pouvez convertir un B::* d'un D::*, bien que vous ne pouvez utiliser l' B::* sur des objets de type D ou sont les descendants de D.

11voto

Matt Price Points 9674

Je ne suis pas sûr à 100% de ce que vous demandez, mais voici un exemple qui fonctionne avec les fonctions virtuelles:

 #include <iostream>
using namespace std;

class A { 
public:
    virtual void foo() { cout << "A::foo\n"; }
};
class B : public A {
public:
    virtual void foo() { cout << "B::foo\n"; }
};

int main()
{
    void (A::*bar)() = &A::foo;
    (A().*bar)();
    (B().*bar)();
    return 0;
}
 

7voto

Charles Bailey Points 244082

Le problème essentiel avec des pointeurs vers les membres, c'est qu'ils peuvent être appliqués à une référence ou un pointeur vers une classe du type correct. Cela signifie que, parce qu' Z est dérivé de l' Y un pointeur (ou de référence) de type pointeur (ou de référence) à l' Y peut en fait montrer (ou reportez-vous) à la classe de base sous-objet de Z ou de toute autre classe dérivée de l' Y.

void (Y::*p)() = &Z::z_fn; // illegal

Cela signifie que quoi que ce soit attribué à un pointeur sur un membre de l' Y doit travailler avec tout Y. Si elle a permis à point à un membre de l' Z (qui n'est pas membre de l' Y) alors qu'il serait possible d'appeler une fonction membre d' Z sur quelque chose qui n'est pas vraiment un Z.

D'autre part, un pointeur de membre de l' Y également de points, le membre de l' Z (héritage signifie qu' Z a tous les attributs et les méthodes de sa base) est, il est légal de convertir un pointeur de membre de l' Y à un pointeur sur un membre de l' Z. C'est à sécurité intrinsèque.

void (Y::*p)() = &Y::y_fn;
void (Z::*q)() = p; // legal and safe

3voto

dagorym Points 2025

Vous voudrez peut-être consulter cet article Les pointeurs de fonction membres et les délégués C ++ les plus rapides possibles La réponse courte semble être oui, dans certains cas.

1voto

Steve Duitsman Points 1334

Je le crois. Étant donné que le pointeur de fonction utilise la signature pour s'identifier, le comportement base / dérivé repose sur l'objet que vous avez appelé.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X