Überschreiben vermeiden.



  • Hallo!

    Ich habe eine Library mit einer Klasse

    class foo
    {
      double bar(double x,double y);
    };
    

    Von dieser Klasse leitet der Client-Code public ab und überschreibt die Methode double bar(double x,double y). Nun ist durch eine Interface-Änderung double bar(double x,double y) nicht mehr sinnvoll und wurde entfernt. Der User, der die Release-Notes nicht liest, wird aber weiterhin eine Funktion double bar(double x,double y) in seiner abgeleiteten Klasse haben, die aber keine Funktion mehr erfüllen kann, was zu schwer auffindbaren Bugs führen würde.

    Frage: Wie läßt sich ein Compile-Fehler erzeugen, wenn der Client-Code die Methode bar überschreibt? Eine erste Idee war die Methode private zu deklarieren, aber das hilft ja nicht.



  • Wenn der Client das override Schlüsselwort benutzt hat, dann gibt der Compiler mindestens eine Warnung aus.

    Aber wieso sollte das schwere Bugs hervorrufen? Im Basisklassen Interface gibt es diese Methode doch nicht, kann demnach dort auch nicht (aus versehen) aufgerufen werden, also kein Grund beunruhigt zu sein.
    Wenn nun irgendwer diese Methode in einem konkreten Unterklassen Objekt aufruft, dann weiss er ja dass es sie gibt und was sie macht.


  • Mod

    Die Methode ist virtuell, nehme ich an? Der Client hätte override verwenden sollen. Ansonsten kann man nicht viel machen.

    Edit: Zu spät; aber override ist kein Schlüsselwort.



  • Arcoth schrieb:

    aber override ist kein Schlüsselwort.

    Echt?

    Visual Studio färbt das so schön blau...
    Aber lässt es doch noch als Variablennamen durchgehen


  • Mod

    Skym0sh0 schrieb:

    Arcoth schrieb:

    aber override ist kein Schlüsselwort.

    Echt?

    Visual Studio färbt das so schön blau...
    Aber lässt es doch noch als Variablennamen durchgehen

    Zurecht. final und override sind normale Bezeichner, die in bestimmten Kontexten eine beondere Bedeutung haben (an diesen Stellen sind Bezeichner sonst nicht erlaubt, somit bleibt die Grammatik eindeutig). Man wollte nicht vorhandenen Code, der diese Bezeichner verwendet, kaputtmachen, deshalb keine neuen Schlüsselwörter. Von der Verwendung in neuem Code als normaler Bezeichner sollte aber abgesehen werden.


  • Mod

    Skym0sh0 schrieb:

    (an diesen Stellen sind Bezeichner sonst nicht erlaubt, somit bleibt die Grammatik eindeutig).

    Ich verstehe nicht, wie der zweite Teil aus dem ersten folgt. Der Kontext selbst kann doch mehrdeutig sein.


  • Mod

    Arcoth schrieb:

    Skym0sh0 schrieb:

    (an diesen Stellen sind Bezeichner sonst nicht erlaubt, somit bleibt die Grammatik eindeutig).

    Ich verstehe nicht, wie der zweite Teil aus dem ersten folgt. Der Kontext selbst kann doch mehrdeutig sein.

    Beispiel?



  • Also ... mittels IDEOne erhalte ich einen Fehler, wenn die Childclass die Methode mit "override" überschreibt.

    #include <iostream>
    using namespace std;
    
    class foo
    {
    	public:
    		virtual double bar(double x);
    };
    
    double foo::bar(double x)
    {
    	return x*x;
    }
    
    class baz : public foo
    {
       	public:
       		double bar(double x, double y) override;
    };
    
    double baz::bar(double x, double y)
    {
    	return x - y;
    }
    
    int main() {
    
    	foo *val1 = new foo();
    	cout << "Foo: " << val1->bar(10) << endl;
    
    	foo *val2 = new baz();
    	cout << "Baz: " <<<< val2->bar(10, 20) << endl;
    	return 0;
    }
    

    prog.cpp:18:13: error: 'double baz::bar(double, double)' marked 'override', but does not override
    double bar(double x, double y) override;

    Und auch wenn override nicht verwendet wird, fällt es doch spätestens beim Aufruf auf die Füße (sofern ein Zeiger auf foo verwendet wird).

    #include <iostream>
    using namespace std;
    
    class foo
    {
    	public:
    		virtual double bar(double x);
    };
    
    double foo::bar(double x)
    {
    	return x*x;
    }
    
    class baz : public foo
    {
       	public:
       		double bar(double x, double y);
    };
    
    double baz::bar(double x, double y)
    {
    	return x - y;
    }
    
    int main() {
    
    	foo *val1 = new foo();
    	cout << "Foo: " << val1->bar(10) << endl;
    
    	foo *val2 = new baz();
    	cout << "Baz: " << val2->bar(10, 20) << endl;
    	return 0;
    }
    

    prog.cpp:32:37: error: no matching function for call to 'foo::bar(int, int)'
    cout << "Baz: " << val2->bar(10, 20) << endl;

    Wird das Objekt als solches instanziiert oder ein Zeiger auf den Typ der Klasse, dann interessiert das ganze den Compiler nicht.

    #include <iostream>
    using namespace std;
    
    class foo
    {
    	public:
    		virtual double bar(double x);
    };
    
    double foo::bar(double x)
    {
    	return x*x;
    }
    
    class baz : public foo
    {
       	public:
       		double bar(double x, double y);
    };
    
    double baz::bar(double x, double y)
    {
    	return x - y;
    }
    
    int main() {
    
    	foo *val1 = new foo();
    	cout << "Foo: " << val1->bar(10) << endl;
    
    	baz *val2 = new baz();
    	cout << "Baz: " << val2->bar(10, 20) << endl;
    	return 0;
    }
    


  • Skym0sh0 schrieb:

    Wenn der Client das override Schlüsselwort benutzt hat, dann gibt der Compiler mindestens eine Warnung aus.

    Aber wieso sollte das schwere Bugs hervorrufen? Im Basisklassen Interface gibt es diese Methode doch nicht, kann demnach dort auch nicht (aus versehen) aufgerufen werden, also kein Grund beunruhigt zu sein.
    Wenn nun irgendwer diese Methode in einem konkreten Unterklassen Objekt aufruft, dann weiss er ja dass es sie gibt und was sie macht.

    Die Methode ist virtuell (hab ich vergessen zu sagen) und wird von der Lib verwendet. Der Client-Code kann sie überschreiben, um eine andere Arbeitsweise der Lib zu erzielen. Und das ist eben jetzt nicht mehr der Fall, deshalb wärs schön gewesen, den Code des Kunden, der das alte Interface verwendet, erst gar nicht compilieren zu lassen.

    Override kannte ich noch nicht, danke. Ich muß mit meinen Libraries auch noch VS2008 und CentOS 6.4 unterstützen, daher ist C++11 bei Libraries kein Thema.



  • Naja, aber du sagtest, die Methode sei nicht mehr gebraucht und wird demnach gelöscht. Dann ist doch immer noch alles gut oO



  • Skym0sh0 schrieb:

    Naja, aber du sagtest, die Methode sei nicht mehr gebraucht und wird demnach gelöscht. Dann ist doch immer noch alles gut oO

    Leider nicht. Der User glaubt die Methode nach wie vor zu überschreiben und damit ein bestimmtes Verhalten der Lib herbeizuführen, aber tatsächlich hat er eine neue Methode deklariert, die von niemandem genutzt wird. Wenn der User ins Manual schauen würde - was keiner tut - würde er sehen, daß es jetzt eine ähnliche Methode dafür gibt, aber mit anderen Parametern.


  • Mod

    Deri schrieb:

    Ich muß mit meinen Libraries auch noch VS2008 und CentOS 6.4 unterstützen, daher ist C++11 bei Libraries kein Thema.

    Ist sowieso eher etwas für Usercode. Kann man im Übrigen recht einfach C++03 kompatibel machen:

    #if __cplusplus < 201103L
    #define final
    #define override
    #endif
    

Log in to reply