2017-11-23 3 views
0

Ich stieß kürzlich auf einige dodgy Code. Aber das Verhalten hat mich ein wenig verblüfft. Das Folgende ist eine Vereinfachung des Problems (im Grunde fehlt das virtuelle Schlüsselwort und macht es nicht-polymorph). Warum/wie wird "C :: foo called, i: 5" gedruckt?Unerwartete C++ - Verhalten in dem folgenden Programm

Wie kann das Objekt im Speicher sogar 'ich' drin haben? Ich habe C++ 03 und C++ 11 ausprobiert.

#include <iostream> 
using namespace std; 

class P 
{ 
public: 
    void foo() 
    { 
     cout << "P::foo called" << endl; 
    } 
}; 

class C : public P 
{ 
public: 
    void foo() 
    { 
     i = 5; 
     cout << "C::foo called, i: " << i << endl; 
    } 

    int i; 
}; 

int main() 
{ 
    C* c = static_cast<C*>(new P()); 
    c->foo(); 
} 

Antwort

1

"Warum/wie druckt es" C :: foo genannt, i: 5 "?"

In static_casting den P Zeiger auf einen C-Zeiger Sie sagen zum complier dieses Stück Speicher interpretieren, als ob es ein Objekt der Klasse C enthalten

Also, wenn Sie foo rufen, dass es nachschlägt Cs-Version von foo.

(Das erklärt die C: foo genannt ... aber nicht, wie ich 5)

"Wie wird das Objekt im Speicher sogar in der Lage zu haben, 'i' in it"

In einer Art und Weise es tut es nicht. Nachdem mir gesagt wurde, dass dieser Speicher ein C enthält, "weiß" der Compiler, dass ich in einem bestimmten Speicher-Offset von den Objekten dieses Zeigers lebe. Dieser Speicher wurde nicht speziell zugewiesen, um ein i-Member für ein Objekt vom Typ C zu enthalten (keine Objekte von C wurden erstellt), aber ihm wurde (falsch) gesagt, dass der Speicher ein C enthält, er wird diese Adresse als verwenden ich, und, weil Sie es in foo setzen, bevor Sie es ausgeben, wird diese widerrechtlich angeeignete Adresse mit 5.

Wenn Sie nicht in foo setzen, aber Sie initialisieren es, wenn es stattdessen erklärt wird, würden Sie Sehen Sie, dass ein reales C-Objekt den Wert ausgibt, auf den Sie es gesetzt haben, aber dass ein P * -Ausdruck als C * ausgibt, was auch immer sich unter dieser Adresse befindet.

0

Das ist genau das erwartete Verhalten, nichts Seltsames damit. „Hinweise static_cast auch eine Funktion zu Zeiger Umstellung auf bestimmte Art Funktion Überlastungen disambiguate verwendet werden können, indem“

http://en.cppreference.com/w/cpp/language/static_cast


Ich bin nicht in der Lage yo überprüfen gerade jetzt, aber in Eine Instanz eines untergeordneten Objekts sollten Sie in der Lage sein, durch Angabe des Namensraums anzugeben, welches der beiden "foo" Sie aufrufen möchten. Ich bin mir nicht sicher, ob ein statischer Cast auftritt.

void C::foo() 
{ 
    if (/*something*/) 
    // do something 
    else 
    D::foo(); 
} 
0

Nach viel Forschung die Schlussfolgerung: „undefinierten Verhalten“

Hier ist die Regel für einziehe static_cast Verwendung in Abschnitt 5.2 gefunden.9 ([expr.static.cast]) des C++ Standard (C++ 0x Formulierung):

A prvalue des Typs "Zeiger auf CV1B", wobei B einen Klasse-Typ ist, kann auf eine prvalue vom Typ umgewandelt werden, „Zeiger auf CV2D“, wobei D ist eine Klasse von B abgeleitet, wenn eine gültige Standard-Konvertierung von „Zeiger auf D“ auf „Zeiger auf B“ existiert, CV2 ist die gleiche cv-Qualifikation als oder größer Lebenslauf-Qualifikation als, cv1 und B ist weder eine virtuelle Basisklasse von noch eine Basisklasse einer virtuellen Basisklasse von D. Der Nullzeigerwert wird in den Nullzeigerwert des Zieltyps konvertiert. Wenn der prvalue des Typs „Zeiger auf CV1B“ verweist auf ein B, die tatsächlich ein Subobjekt eines Objekts des Typs D, den resultierenden Zeiger zeigt auf das einschließende Objekt vom Typ D. Andernfalls ist das Ergebnis der Besetzung undefiniert.

ref: - Downcasting using the 'static_cast' in C++