2016-05-16 8 views
0

Ich teste ein C++ - Programm (demonstriert ein Dekorator-Entwurfsmuster), fand aber ein seltsames Problem. Ein Beispielcode ist wie folgt. Die Fehlerzeile hatte versehentlich einen zusätzlichen new Ausdruck, aber erstaunlicherweise kompiliert der Code und läuft mit Fehlerausgabe (der Dekorator wurde zweimal ausgegeben).C++: Neu gibt einen Typ zurück, kompiliert aber ok

$ ./a.out 
simple window with scroll bar with scroll bar 

Was ist hier passiert?

#include <iostream> 
#include <string> 

using namespace std; 

class Window { 
public: 
    virtual string desc() = 0; 
    virtual ~Window() {} 
}; 

class SimpleWindow : public Window { 
public: 
    string desc() { return "simple window"; } 
}; 

class WindowDecorator : public Window { 
protected: 
    Window *window; 

public: 
    WindowDecorator(Window *window) : window(window) {} 
}; 

class ScrollBar : public WindowDecorator { 
public: 
    ScrollBar(Window *window) : WindowDecorator(window) {} 
    string desc() { return window->desc() + " with scroll bar"; } 
}; 

int main() 
{ 
    ScrollBar scrollBar = new ScrollBar(new SimpleWindow()); // error line 
    cout << scrollBar.desc() << endl; 

    return 0; 
} 
+4

Könnten Sie genauer beschreiben, welcher Teil davon Sie überrascht? – juanchopanza

+0

Kann ich Ihnen empfehlen, von den Zeigern ein wenig zurückzukommen und RAII ein bisschen mehr zu suchen/zu üben? Diese Speicherlecks tun mir weh. –

+0

Es sollte entweder lauten: ScrollBar scrollBar = ScrollBar (new SimpleWindow()); oder ScrollBar * scrollBar = neue ScrollBar (neues SimpleWindow()); Die Frage ist, warum der "neue" Operator in der Fehlerzeile einen Zeiger zurückgibt, aber kompiliert (mit Fehlerausgabe). – user2847598

Antwort

3

Was Sie hier haben, ist ein Beispiel dafür, warum wir explicit Schlüsselwort für Konstruktoren in C++ verwenden. (Siehe cppreference.com)

Sie sehen, Konstrukteuren mit einem einzigen Parameter, wie zum Beispiel das in Ihrem ScrollBar Klasse ...

ScrollBar(Window *window) : WindowDecorator(window) {} 

... es sei denn als explicit markiert, wird vom Compiler verwendet werden, auszuführen implizite Konvertierungen, in Ihrem Fall von Window * zu ScrollBar. Ein solcher Konstruktor wird auch Converting Constructor genannt.

Also, was passiert, in dieser Linie ...

ScrollBar scrollBar = new ScrollBar(new SimpleWindow()); 

... ist Compiler glücklich (implizit) Ihre ScrollBar(Window *window) Konstruktor noch einmal aufrufen ScrollBar * zu konvertieren, erhalten Sie von new ScrollBar(), zu ScrollBar. Dies funktioniert, weil:

  1. ScrollBar eine Unterklasse von Window (durch WindowDecorator) ist, und so ScrollBar * implizit in Window * umgewandelt werden.
  2. Constructor ScrollBar(Window* window) hat einen einzigen Parameter und ist nichtexplicit markiert, so dass Sie eine Instanz von ScrollBar aus dem Zeiger von new ScrollBar() zurückgegeben.

Dies ist wahrscheinlich nicht das, was Sie im Sinn hatte, wenn der Konstruktor schreiben, so dass Sie wahrscheinlich umschreiben sollte es so:

explicit ScrollBar(Window *window) : WindowDecorator(window) {} 

Diese die überraschende Linie führen sollte einen Kompilierungsfehler verursachen . Es ist generell eine gute Idee in C++, explicit immer nur für Konstruktoren mit einem Argument zu verwenden und diese nur zu entfernen, wenn Sie absichtlich beschließen, implizite Konvertierungssemantiken zuzulassen.

1

Gemäß der C++ Standard (4,10 Pointer Conversions)

3 A prvalue des Typs „Zeiger auf cv D“, wobei D ein Klassentyp ist, kann mit einer prvalue von umgewandelt Typ „Zeiger auf cv B“, wobei B eine Base Klasse (Ziffer 10) von D ....

und (4 Standard-Umwandlungen)

1 Standardkonvertierungen sind implizite Konvertierungen mit eingebauter Bedeutung. Abschnitt 4 zählt die vollständige Menge solcher Umwandlungen auf. A Standardumwandlungsfolge ist eine Folge von Standardumwandlungen in die folgende Reihenfolge:

....

- keine oder eine Umwandlung aus dem folgenden Satz: Integral Promotions, Punkt Förderung Floating , Integralkonvertierungen, Gleitkomma Konvertierungen, Floating-Integral-Konvertierungen, Zeigerkonvertierungen, Zeiger auf Elementkonvertierungen und boolesche Konvertierungen.

In dieser Erklärung

ScrollBar scrollBar = new ScrollBar(new SimpleWindow()); 

in der rechten Seite befindet sich ein Zeiger der abgeleiteten Klasse, die ScrollBarWindow auf einen Zeiger der Basisklasse umgerechnet werden kann.

Die Klasse ScrollBar hat Konvertierungskonstruktor

ScrollBar(Window *window) : WindowDecorator(window) {} 

, die oben in der Erklärung genannt wird.

+0

Danke für die Info und Erklärung. Ich wünschte, ich könnte beide Antworten akzeptieren. – user2847598

Verwandte Themen