2010-04-01 13 views
13

je reçois une erreur de compilation, que je suis un peu confus au sujet. C'est sur VS2003.Pourquoi un membre de superclasse protégé n'est pas accessible dans une fonction de sous-classe lorsqu'il est passé en argument?

erreur C2248: 'A :: y': ne peut pas accéder à un membre protégé déclaré dans la classe 'A'

class A 
{ 
public: 
    A() : x(0), y(0) {} 
protected: 
    int x; 
    int y; 
}; 

class B : public A 
{ 
public: 
    B() : A(), z(0) {} 
    B(const A& item) : A(), z(1) { x = item.y;} 
private: 
    int z; 
}; 

Le problème est avec x = item.y;

L'accès est spécifié comme protégé. Pourquoi le constructeur de la classe B n'a-t-il pas accès à A :: y?

Répondre

5

Il est pour cette raison:

class base_class 
{ 
protected: 
    virtual void foo() { std::cout << "base::foo()" << std::endl; } 
}; 

class A : public base_class 
{ 
protected: 
    virtual void foo() { std::cout << "A::foo()" << std::endl; } 
}; 

class B : public base_class 
{ 
protected: 
    virtual void foo() { std::cout << "B::foo()" << std::endl; } 

public: 
    void bar(base_class *b) { b->foo(); } 
}; 

Si cela était légal, vous pouvez le faire:

A a; 
B b; 
b.bar(&a); 

Et vous appellerez un membre protected de A à B, qui n » t autorisé.

+0

peut-être, vous avez oublié 'virtual'? –

+1

Pavel: oops, ouais, j'ai ajouté le mot-clé 'virtual' ... ça ne change pas vraiment mon point, cependant :) –

1

documentation IBM résume le mieux:

Un membre protégé classe de base nonstatic peut être consulté par les membres et amis de toutes les classes dérivées de cette classe de base en utilisant l'un des suivants:

  • un pointeur vers une classe
  • provenant directement ou indirectement une référence à une classe dérivée directement ou indirectement
  • Un objet d'un directement ou indirectement classe dérivée

Ainsi, en utilisant votre exemple ci-dessus comme base:

B::B(const A& item) : A(), z(1) { 
    // NOT OK because `item` is not a reference to the derived class B 
    //int i = item.y; 

    // OK because `item` reinterpreted as a reference to the derived class B 
    // Do not do this (bad!) -- for illustrative purposes only 
    int i = reinterpret_cast< const B& >(item).y; 

    // OK because it is equivalent to `this->x = i`, 
    // where `this` is a pointer to the derived class B 
    x = i; 
} 
3

Les autres réponses expliquent le raisonnement derrière la prévention de votre objet B d'accéder à les parties de A protégées dans votre exemple, même si B « est-a » A. Bien sûr, la meilleure façon de résoudre ce problème est de rendre les parties de A you want access to public` ou ont des méthodes d'accès accessibles au public.

Cependant, vous pouvez décider que est inappropriée (ou vous pourriez ne pas avoir le contrôle sur la définition de A). Voici quelques suggestions pour vous permettre de contourner le problème, en augmentant l'ordre de subvertir le contrôle d'accès de A. Notez que toutes ces solutions de contournement supposent que est constructible par copie.

Dans le premier cas, vous utilisez simplement le constructeur de copie pour A de mettre en place un état initial pour cette partie de l'objet B, puis le corriger après:

class B1 : public A 
{ 
public: 
    B1() : A(), z(0) {} 
    B1(const A& item) : A(item), z(1) { 
    // fix up the A sub-object that was copy constructed 
    // not quite the way we wanted 
    x = y; 
    y = 0; 
    } 
private: 
    int z; 
}; 

Je trouve que très confus et probablement très sujette aux erreurs (en supposant que nous voulons que le sous-objet à être différent dans l'objet BA que l'objet A étant transmis au constructeur - une situation inhabituelle, mais il est ce qui a été donné dans le problème).Cependant, le fait que cela peut être fait donne une justification pour les exemples les plus subversives qui suivent ...

L'exemple suivant crée un objet B temporaire qui a une copie exacte de l'objet que nous voulons A accès. On peut alors utiliser l'objet B temporaire pour accéder aux éléments qui ont été protégés:

class B2 : public A 
{ 
public: 
    B2() : A(), z(0) {} 
    B2(const A& item) : A(), z(1) { 
    // create a special-use B2 object that can get to the 
    // parts of the A object we want access to 
    B2 tmp(item, internal_use_only); 

    x = tmp.y; // OK since tmp is of type B 
    } 

private: 
    int z; 

    // create a type that only B2 can use as a 
    // 'marker' to call a special constructor 
    // whose only purpose in life is to create 
    // a B object with an exact copy of another 
    // A sub-object in it 
    enum internal_use { 
    internal_use_only 
    }; 
    B2(const A& item, internal_use marker) : A(item), z(0) {}; 
}; 

Je trouve cette solution à être un peu moins confus que le premier, mais il est encore confus (à mon avis). Avoir une version bastarde de l'objet B juste pour accéder aux parties de l'objet A que nous voulons est étrange.

Nous pouvons faire quelque chose à ce sujet en créant un proxy spécial pour A objets qui donne l'accès que nous voulons. Notez que c'est la solution de contournement la plus subversive car c'est quelque chose que n'importe quelle classe peut faire pour accéder aux parties protégées de A, même si elles ne sont pas des sous-classes de A elles-mêmes. Dans le cas de la classe B, il y a une certaine légitimité pour obtenir les pièces protégées de A objets, car B est-un A, et comme nous l'avons déjà vu il y a des solutions de contournement qui nous permettent d'obtenir un accès qui utilisent les droits seulement qui class B déjà a, donc je considère que c'est une version plus propre de ces solutions de contournement dans le cas de class B.

class B3 : public A 
{ 
public: 
    B3() : A(), z(0) {} 
    B3(const A& item) : A(), z(1) { 
    // a special proxy for A objects that lets us 
    // get to the parts of A we're interested in 
    A_proxy tmp(item); 
    x = tmp.get_y(); 
    } 

private: 
    int z; 

    class A_proxy : public A 
    { 
    public: 
     A_proxy(const A& other) : A(other) {}; 
     int get_x() {return x;}; 
     int get_y() {return y;}; 
    }; 

}; 
+0

Merci pour cette réponse. En effet, votre hypothèse était correcte. Je voulais que le sous-objet A de l'objet B soit différent de l'objet A transmis au constructeur. –

+0

Dans le scénario de la vie réelle, toutes les informations que j'avais besoin de copier étaient déjà disponibles via l'interface publique de A, donc j'ai simplement utilisé cela au lieu d'accéder aux membres protégés. En fait, le membre protégé A était à l'origine privé et je l'ai protégé pour pouvoir y accéder depuis B. –

+0

@Michael Burr - Y a-t-il un moyen de changer le membre protégé de A directement (pas la copie de A comme vous l'avez montré)? – Liton