2010-08-17 14 views
11
class Foo { 
public: 
static const int kType = 42; 
}; 

void Func() { 
Foo *bar = NULL; 
int x = bar->kType; 
putc(x, stderr); 
} 

Ist dieses definierte Verhalten? Ich habe den C++ - Standard gelesen, konnte aber nichts über den Zugriff auf einen statischen const-Wert finden ... Ich habe die Assembly von GCC 4.2, Clang ++ und Visual Studio 2010 untersucht und keine von ihnen führt eine Dereferenzierung des NULL durch Zeiger, aber ich möchte sicher sein.C++ statischer const-Zugriff durch einen Nullzeiger

+1

Die meisten Compiler sollten Sie über einen Instanzzeiger auf den Zugriff auf statische/const-Member warnen. – cHao

Antwort

10

Sie können einen Zeiger (oder einen anderen Ausdruck) verwenden, um auf ein statisches Element zuzugreifen. Allerdings ist dies leider durch einen NULL-Zeiger offiziell undefiniert. Von 9.4/2 "Statische Member":

Ein statisches Element s der Klasse X kann die qualifizierte Nummer Ausdruck verwendet bezeichnet X :: s; Es ist nicht notwendig , die Klassenmemberzugriffssyntax (5.2.5) zu verwenden, um auf ein statisches Element zu verweisen. Ein statisches Element kann unter Verwendung von der Klassenmember-Zugriffssyntax in bezeichnet werden. In diesem Fall wird der Objektausdruck ausgewertet.

am Beispiel Basierend, der folgt:

ist
class process { 
public: 
    static void reschedule(); 
}; 

process& g(); 

void f() 
{ 
    process::reschedule(); // OK: no object necessary 
    g().reschedule();  // g() is called 
} 

Die Absicht, Sie zu ermöglichen, dass in diesem Szenario genannt werden Funktionen zu gewährleisten.

+4

Ich wollte ein Haar aufspalten und darauf hinweisen, dass "das Auswerten des Objektausdrucks" nicht notwendigerweise eine * Dereferenzierung des Zeigers * bedeutet - es könnte nur bedeuten, den Zeigerwert zu finden - aber wenn man sich §5.2.5 anschaut, erscheint es dass es * tut *, weil der "object-expression" in diesem Fall definiert ist als "* bar", nicht nur "bar". Also hast du recht. – zwol

+0

War das nicht ein Thema der Debatte? Der Begriff der Bewertung eines Objekts, wie es einfach nominiert wird, ist eher zen. Auf der anderen Seite zeigt das Beispiel unter diesem Text deutlich einen Funktionsaufruf. Offensichtlich meinen sie, dass der Ausdruck vor dem "." oder "->" wird ausgewertet. – Potatoswatter

+2

@Potatoswatter: Ich glaube, dass meine Interpretation richtig ist, aber ich betrachte mich sicherlich nicht als Autorität. Während ich denke, dass die Wahrscheinlichkeit, eine statische Member-Funktion über einen entsprechend typisierten NULL-Zeiger aufzurufen, wahrscheinlich wie erwartet funktionieren wird, ist es einfach genug, den Aufruf mit dem Typnamen und nicht mit einem Zeiger zu machen (obwohl ich für Templates denke) das ist vielleicht nicht immer wahr), also ist es besser, einfach keine Zeiger dafür zu verwenden. –

3

Ich glaube, dass der Ist-Wert des Typs nicht verwendet wird, wenn

bar->kType 

seit kType statisch Aufruf, und die Bar ist vom Typ Foo es das gleiche ist

Foo::kType 
als Aufruf

was man eigentlich zur besseren Übersicht sowieso machen sollte.

Aufruf bar->kType gibt eine Compiler-Warnung auf den meisten Plattformen aus diesem Grund.

+0

+1 "was Sie eigentlich aus Gründen der Klarheit tun sollten" – leedm777

+0

Korrekte, statische Member werden unabhängig von Objektinstanzen gespeichert, so dass das Objekt selbst niemals dereferenziert wird. – casablanca

+0

@casablanca: Das ist ein Implementierungsdetail. Wenn Sie einen Beweis dafür haben, dass dies das Verhalten Ihrer speziellen Compiler-Version ist, dann ist das großartig! Ansonsten tu es nicht. In Bezug auf die Frage nach der abstrakten Maschine namens C++ ist das natürlich keine "richtige" Antwort. –

1

Abgesehen von der Frage über Zeiger durch den NULL zugreifen, gibt es einen weiteres subtiles Problem im Code

$ 9.4.2/2 - „Die Deklaration eines statischen Datenelementes in seiner Klassendefinition ist keine Definition und kann von einem unvollständigen Typ sein, der nicht cv-qualifiziert ist. Die Definition für ein statisches Datenelement muss in einem Namespace-Gültigkeitsbereich erscheinen, der die Klassendefinition des Elements enthält. "

$ 9.4.2/4- "Wenn ein statisches Datenelement vom Const-Integral- oder Const-Aufzählungstyp ist, kann seine Deklaration in der Klassendefinition einen Konstanteninitialisierer angeben, der ein ganzzahliger Konstantenausdruck sein soll (5.19) In diesem Fall kann das Member in ganzzahligen Konstantenausdrücken auftreten. Das Member muss weiterhin in einem Namespacebereich definiert sein, wenn es im Programm verwendet wird, und die Namespacebereichsdefinition darf keinen Initialisierer enthalten. "

class Foo { 
public: 
static const int kType = 42; 
}; 

int const Foo::kType; 

void Func() { 
Foo *bar = NULL; 
int x = bar->kType; 
putc(x, stderr); 
} 

Also noch ein Grund für UB im OP-Code.

1

Selbst wenn es funktioniert, ist es schrecklich Code.

In seriöser Programmierung codieren Sie nicht nur für sich selbst, sondern auch für andere, die Ihren Code pflegen. Das Spielen solcher Tricks muss vermieden werden, weil Sie Ihre Kollegen respektieren.

Eine Konsequenz dieses Codes: Ob der Zeiger NULL ist oder nicht, ist auch nicht in Frage, aber es bedeutet, dass dieses Mitglied kType kein einfaches nicht-statisches Mitglied der Klasse sein darf. Manchmal sind Klassen groß (das ist auch böse) und man kann nicht immer die Definition jeder einzelnen Variablen überprüfen.

Seien Sie streng. Und nennen Sie alle Ihre statischen Mitglieder nur auf diese Weise:

Foo::kType 

Eine andere Möglichkeit ist es, eine Codierung Konvention zu folgen, die wissen zu lassen, dass das Element statisch ist, beispielsweise ein s_ Präfix für alle Klassen statische Mitglieder:

Foo::s_kType 
-1

Es gibt sozusagen eine höhere Regel, die im Grunde sagt - denken Sie nicht einmal über die Zusammenstellung von Dingen nach, die nachweislich nicht verwendet werden. Die fortgeschrittene Template-Programmierung hängt davon sehr ab, so dass selbst wenn es ein bisschen grau-zonisch ist, wenn ein Compiler klar sieht, dass das Ergebnis eines Konstrukts nicht verwendet wird, es es nur beseitigen wird. Vor allem, wenn es nachweislich sicher ist wie in diesem Fall.

Sie können versuchen, ein paar Varianten, wenn Sie wollen - wie machen Zeiger ein Parameter einer Funktion, Ergebnis einer Funktion, lassen einen Zeiger nicht initialisiert (beste Chance für Compiler-Beschwerde zu lösen), eine gerade Besetzung von 0 (beste Chance, sich nicht zu beschweren).

+0

Das ist albern. Alle möglichen Optimierungen können dazu führen, dass Ihr Funktionsaufruf nicht einmal stattfindet oder [andere verrückte Sachen] (http://blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx). Es ist mein ernst. Dies kann in der Praxis passieren. –

Verwandte Themen