2010-07-22 5 views
1
class Base{ 
    public: 
     Base(int val):_id(val){}; 
     int _id; 
}; 

class Derived : Base { 
    public: 
     Derived(int val):Base(_id+val){}; 
}; 

int main(){ 
    Derived d(60); 
} 

pourquoi cela ne génère-t-il pas une erreur? La classe de base n'est toujours pas construite mais je suis capable d'utiliser '_id'?Héritage, Appeler des cteurs de classe de base

grâce

Répondre

1

Ce n'est pas construit, mais sa mémoire a été alloué et donc existe _id et détient une valeur non initialisée.

0

Je ne comprends pas la question. Cela fonctionne parce que la norme C++ permet ce genre de comportement. La mémoire de la classe a déjà été allouée à ce moment, il est donc possible d'utiliser cette variable.

+1

Je ne comprends pas que vous ne pouvez pas comprendre la question – stijn

2

Un exemple plus simple: int x = x + 1; vous montre que les compilateurs C++ ne suivent pas l'initialisation des variables. Dans votre exemple, _id existe en mémoire (il a une adresse), mais n'a jamais été initialisé. Cependant, puisque le compilateur ne suit pas, il n'y a pas d'erreur.

0

La classe de base n'est toujours pas construite mais je suis capable d'utiliser '_id'?

Non, vous ne l'utilisez _id, parce qu'il n'y a pas un objet _id encore. Mais il y a la mémoire brute de l'objet, et vous pouvez y accéder en utilisant l'identifiant, en interprétant la mémoire brute comme un objet int.

invoque Faire de comportement non défini et vous feriez mieux de ne pas le faire. Un comportement non défini peut conduire à ce que votre HD soit formatée, que vous tombiez enceinte ou que le programme fonctionne correctement et fasse ce qu'il est censé faire. Et vous ne savez jamais lequel c'est, car il pourrait être différent avec chaque compilateur, version du compilateur, phase de lune, ou autre.

Dans le cas ci-dessus, l'interprétation de la mémoire brute en tant qu'objet int a pour résultat commun que tout modèle binaire existant à cette position est interprété comme un entier et que le résultat est utilisé. Cependant, les plates-formes pourraient tout aussi bien intercepter l'accès à la mémoire non initialisée et lancer une exception matérielle.
Si _id était un non-POD (std::string), un résultat probable, mais non garanti, constituerait une violation d'accès.

Modifier en réponse à un commentaire:

Vous pouvez accéder aux membres de la classe de base, même non initialisées, dans une liste d'initialisation classe dérivée très bien:

#include <iostream> 

class Base{ 
public: 
    Base(int val):id_(val){}; 
protected: 
    int id_; 
}; 

class Derived : Base { 
public: 
    Derived(int val):Base(id_+val), blah_(id_) {}; 
    int blah() const {return blah_;} 
private: 
    int blah_; 
}; 

int main(){ 
    Derived d(60); 
    std::cout << d.blah() << '\n'; 
    return 0; 
} 
+0

OK, et l'étendue dans laquelle il essaie de trouver _id est l'étendue de classe de base en raison de l'appel du constructeur de classe de base? Si j'essaie d'accéder à _id dans la liste d'initialisation de derived (sans appeler explicitement ctor de base), j'obtiens une erreur. – tuco

+0

@tuco: Il n'y a rien de faux __syntactically__ avec l'accès à un membre de la classe de base dans une liste d'initialisation de la classe dérivée. Voir ma réponse, j'ai ajouté du code qui compile très bien. – sbi

+0

C'est cool. Je me suis rendu compte que j'essayais d'initialiser le membre de classe de base dans la liste d'initialisation de derived et d'obtenir l'erreur. – tuco

0

il y avait Imaginez un malloc invisible au point où vous avez tapé le : pour démarrer la liste d'initialisation. Cela vous donnerait un bloc de mémoire non initialisé assez grand pour contenir un objet Derived, y compris la partie Base. Le compilateur connaît le décalage auquel fait référence _id, il vous donne donc volontiers la valeur indésirable qui y réside.

Il est possible de référencer _id après l'appel à Base() dans la liste d'initialisation. Les compilateurs ne font pas de cas particulier lorsque le constructeur de la classe de base n'a pas encore été appelé.

0

Ce serait une erreur si vous avez correctement déclaré votre attribut de membre private. Mélanger des données publiques avec l'héritage comme ceci va probablement seulement causer la confusion ou le comportement incorrect tel que vous avez observé. Si vos membres étaient privés, le compilateur empêcherait cette erreur ET cela améliorerait l'encapsulation de votre classe.

EDIT: La raison pour laquelle _id est utilisable est que sa mémoire a été allouée, elle n'a tout simplement pas encore été initialisée. Pensez quelque chose le long des lignes de:

int x; 
int y = x; 

Vous avez aucune idée de ce qui sera en y parce que x n'a jamais été initialisé.