2010-01-12 11 views
1

Comme vous pouvez le voir dans le code ci-dessous, j'ai une classe de base abstraite "HostWindow" et une classe qui en dérive "Chrome". Toutes les fonctions sont implémentées dans Chrome. Le problème est que je ne peux pas appeler des fonctions dans Chrome si elles sont virtuelles.Classe héritée "erreur de pointeur non valide" lors de l'appel de fonctions virtuelles

class HostWindow : public Noncopyable { 
public: 
    virtual ~HostWindow() { } 

    // Pure virtual functions: 
    virtual void repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false) = 0; 
    virtual void scrollbarsModeDidChange() const = 0; 
} 

class Chrome : public HostWindow { 
    // HostWindow functions: 
    virtual void repaint(const IntRect&, bool contentChanged, bool immediate = false, bool repaintContentOnly = false); 
    virtual void scrollbarsModeDidChange() const; 

    void focus() const; 
} 

Disons donc que nous avons une instance de Chrome, et nous appelons quelques fonctions:

WebCore::Chrome *chrome = new Chrome(); 
chrome->repaint(IntRect(), true); // Null pointer error 
chrome->focus(); // returns void (works) 

L'erreur de pointeur NULL je reçois chaque fois que j'appelle des fonctions virtuelles est:

Le programme a reçu le signal EXC_BAD_ACCESS, Impossible d'accéder à la mémoire. Raison: KERN_PROTECTION_FAILURE à l'adresse: 0x00000008

Toute idée de ce qui se passe?

Mise à jour: Comme beaucoup d'entre vous l'ont fait remarquer - ce code fonctionne réellement. Malheureusement, je ne peux pas donner un exemple plus complet, car le code est profond dans WebCore (WebKit). Cependant, j'ai réduit le problème. Si je crée une instance Chrome manuellement, l'appel des fonctions virtuelles fonctionne. Donc, le problème est avec cette instance de chrome particulière - il ne peut pas instancié correctement. Maintenant, l'instance de Chrome est instanciée dans un constructeur d'une autre classe. Je vais étudier plus en détail ...

Mise à jour 2: Ok, l'examen de la vtable sur l'instance incriminée montre que c'est null; à partir de GDB:

p *(void **)chrome 
$52 = (void *) 0x0 

Une instance normale a une vtable correcte. Donc, je dois déterminer pourquoi le vtable est nul - je me demande comment cela pourrait arriver? Peut-être parce qu'il est instancié dans d'autres classes constructeur?

Mise à jour 3: On dirait que j'ai raison à propos de l'instanciation dans un autre constructeur de classe.

Ainsi, avant l'instanciation ressemblait à ceci:

Page::Page(ChromeClient* chromeClient, ...) 
    : m_chrome(new Chrome(this, chromeClient)) 

Et m_chrome est une instance non valide, avec un vtable nul. J'ai changé l'instanciation de sorte qu'il se produit lorsque la première fois que la variable est nécessaire (ce qui implique l'enregistrement ChromeClient pour plus tard):

Page::Page(ChromeClient* chromeClient, ...) 
    : m_chrome(0) 
    , m_chrome_client(chromeClient) 

Chrome* Page::chrome() const { 
    if(!m_chrome) { 
    m_chrome = new Chrome(this, m_chrome_client); 
    } 
    return m_chrome; 
} 

Maintenant, la page :: instance chrome() est correcte, avec le vtable propre - plutôt étrange!

Mise à jour 4: Dernière mise à jour, je promets :). Ok, donc je l'ai localisé exactement. Vous obtenez l'instance correcte, avec vtable, si vous l'instanciez dans le corps du constructeur de Page. Si vous l'instanciez dans la tête du constructeur de Page, il n'a pas de vtable. Y a-t-il une limitation dans les types de paramètres de variables que vous pouvez faire dans la tête d'un constructeur? Je suppose que c'est une autre question Stackoverflow.

Merci les gars d'être si utile.

+2

Veuillez publier le code REAL en utilisant copier et coller. –

+0

Ceci est le code réel. Je ne peux pas le rendre plus succinct car c'est juste au milieu de WebKit. –

+0

Quelle est la valeur de 'chrome' immédiatement après l'appel 'WebCore :: Chrome * chrome = new Chrome();' Si NULL, une exception est-elle levée? –

Répondre

2

Oui, le pointeur 'this' est zéro. Ajoutez 8 pour obtenir un décalage, et c'est votre faute. Vous n'avez apparemment aucun objet réel.

Puisque vous n'avez pas posté assez de code pour vraiment comprendre, je devine. Soit le pointeur entier est 0, soit le pointeur de la table de fonction virtuelle est 0, peut-être parce que l'objet a été supprimé après sa création et avant que vous ne l'appeliez.

Le meilleur conseil que je peux vous donner est de créer un tube à essai beaucoup plus petit. Soit vous trouverez votre problème ou vous vous retrouverez avec un exemple postable.

La vtbl n'est pas en place dans une instance jusqu'à la fin du processus de construction. En fait, la spécification nécessite une modification progressive de vtbl pour correspondre à l'état de construction de la hiérarchie de classe.

+0

Pourriez-vous élaborer?Je suis assez nouveau en C++. J'ai une instance, Chrome, et je peux appeler certaines fonctions - comme focus(), mais pas celles définies dans l'ABC, comme repaint(). –

+0

'Chrome' n'est pas l'instance, c'est la classe. 'chrome' est l'instance. –

+0

Le vtable est nul - voir Mises à jour. Je n'ai aucune idée de comment cela pourrait se passer. –

0

Pouvez-vous poster le code complet?

Après une légère modification dans votre code (ce qui est disponible), il fonctionne:

#include <iostream> 

class HostWindow { 
public: 
    virtual ~HostWindow() { } 

    // Pure virtual functions: 
    virtual void repaint(const int , bool contentChanged, bool immediate = false, bool repaintContentOnly = false) = 0; 
    virtual void scrollbarsModeDidChange() const = 0; 
}; 

class Chrome : public HostWindow { 
public: 
    // HostWindow functions: 
    virtual void repaint(const int , bool contentChanged, bool immediate = false, bool repaintContentOnly = false) 
    { 
     std::cout << "In repaint." << std::endl; 
    } 
    virtual void scrollbarsModeDidChange() const { } 

    void focus() const 
    { 
     std::cout << "In focus." << std::endl; 
    } 
}; 

int main() 
{ 
    Chrome *chrome = new Chrome(); 
    chrome->repaint(1, true); // Null pointer error 
    chrome->focus(); 
    delete chrome; 
    return 0; 
} 
0

Je ne suis pas au courant de la base de code que vous avez, mais vous ne devriez pas écrire:

// note the 'WebCore::Chrome()' 
WebCore::Chrome *chrome = new WebCore::Chrome(); 
chrome->repaint(IntRect(), true); // 'chrome' should be a valid pointer now 

au lieu de:

WebCore::Chrome *chrome = new Chrome(); 
chrome->repaint(IntRect(), true); // Null pointer error 
+0

Oui, c'était une faute de frappe dans l'exemple. –

+1

@Alex: C'est pourquoi vous devriez toujours couper/coller du code. __NEVER__ retype car il ajoute juste des erreurs et entraîne beaucoup de travail nul pour les personnes qui vous aident. –

0

ssume votre incopiable sont les suivantes (au moins pour le mien a fait)

class NonCopyable 
{ 
protected: 
    NonCopyable() {} 
    ~NonCopyable() {} 
private: 
    NonCopyable(const NonCopyable&); 
    const NonCopyable& operator=(const NonCopyable&); 
}; 

après l'insertion modificateur du public à la fonction de la classe chrome et une mise en œuvre factice pour eux, tout cela a fonctionné sans problème posé.

il n'y a pas de problème avec le code affiché, il est peut-être que vous faites des choses mauvaises et ne pas poster ceux qui font partie ici.

enfin, ne la vérification de l'échec de l'allocation. (oui, "nouveau" est l'allocation sur le tas)

+0

Avez-vous lu les mises à jour? –

+0

malheureusement, je l'ai fait ... quelle est la classe Page? Vous avez ajouté un extrait de code sans détails pour que les autres puissent le comprendre – YeenFei

0

J'ai trouvé que cela était provoqué en permettant à tous les symboles d'être exportés.

Habituellement, WebCore n'a qu'un sous-ensemble de symboles exportés - essentiellement sur des choses dont WebKit a besoin.

J'ai changé cela pour exporter chaque symbole - et cela a causé cette erreur.