C++ Defaultparameter

Freezedevil

Lieutenant
Registriert
Mai 2011
Beiträge
640
Hi,

ich möchte eine bestehende Methode um einen Defaultparameter erweitern. Dabei möchte ich jedoch erreichen, dass bestehende Aufrufe gültig bleiben, ich die Methode jedoch mit dem zusätzlichen Parameter aufrufen kann, jedoch trotzdem von den bestehenden Defaultparametern Gebrauch machen kann. Am Beispiel wird denke ich deutlich worauf ich hinaus will.

bestehende Methode
Code:
void m(string s1, string s2, string s3="foo", string s4="bar");

Ich möchte jetzt noch einen bool mitgeben und mir sind dafür die folgenden Möglichkeiten eingefallen:
Code:
a)
void m(string s1, string s2, bool b=false, string s3="foo", string s4="bar");

b)
void m(string s1, string s2, string s3="foo", string s4="bar", bool b=false);

c)
void m(string s1, string s2, bool b) {
    m(s1, s2, "foo", "bar", b);
}
void m(string s1, string s2, string s3="foo", string s4="bar", bool b=false);

Variante a) bricht möglicherweise die Kompatibilität zu bestehenden Aufrufen
Variante b) zwingt mich dazu s3 und s4 zu übergeben wenn ich b übergeben will -> selbes Problem wie c)
Variante c) falls sich die Standardbelegung ändert hab ich jetzt zwei Stellen an denen ich sie ändern muss

Irgendwie stellen mich alle drei Varianten nicht zufrieden, wobei c) anscheinend noch die beste ist.

Von dem Thread erhoffe ich mir weitere Ideen/Vorschläge wie ich mein Problem lösen kann.
 
Polymorphy ist die Antwort:
2 Methoden:
deklaration:
void m(string s1, string s2, string s3=DEFAULTS3, string s4=DEFAULTS4, bool b=false);
void m(string s1, string s2, bool b); // b kann kein default haben sonst wäre es nicht eindeutig bei m("foo","bar");

definition
void m(string s1, string s2, string s3, string s4, bool b){
// wie bisher
}
void m(string s1, string s2, bool b){
m(s1,s2,DEFAULTS3,DEFAULTS4,b);
}

wichtig ist, dass bitte darauf geachtet wird, die default werte nicht "hardcoded" reinzuschreiben, sonst hat man später evtl. mal ein problem.
 
@ABC_Freak
Das nennt sich aber nicht Polymorphie sondern "function overloading" aber der Ansatz stimmt schon.
Ich würde es wie folgt machen.
Declaration
Code:
void m(std::string, std::string, std:string, std::string);
void m(std::string, std::string, bool);
void m(std::string, std::string, std:string, std::string, bool);
Definition
Code:
void m(std::string, s1, std::string s2, std::string s3, std::string s4){
	m(s1, s2, s3, s4, false);
}

void m(std::string s1, std::string s2, bool b){
	m(s1, s2, "foo", "bar", b);
}

void m(std::string, s1, std::string s2, std::string s3, std::string s4, bool b){
	//Do stuff here
}

Somit bleibt die Signatur deiner bisherigen Funktion gleich und du brauchst nichts an dem Code ändern
 
Ich hab vorhin leider verschwiegen, dass es sich bei mir um Konstruktoren handelt. Mir war heute morgen leider noch nicht bewusst, dass das ein Rolle spielt. Sorry.
Dadurch scheiden die Methoden mit dem gegenseitig aufrufen leider aus, da ich kein C++11 (und somit delegate constructors) nutzen kann. Oder hab ich was uebersehen?
 
Du könntest immer noch die in den Konstruktoren erledigte Arbeit in eine Art init-Methode auslagern und die dann aus den diverseren Konstruktoren heraus aufrufen. Das wäre zwar eventuell etwas unschön, da du so möglicherweise einige Initialisierungen nicht mehr in der Initialisierungsliste des Konstruktors vornehmen könntest, aber was anderes fällt mir im Moment nicht ein.
 
Zuletzt bearbeitet: (ausrufen => aufrufen)
antred schrieb:
Du könntest immer noch die in den Konstruktoren erledigte Arbeit in eine Art init-Methode auslagern........ aber was anderes fällt mir im Moment nicht ein.

Mir fällt noch was ein, wenn auch etwas umständlicher :p Man könnte den bisherigen Code in eine neue Basisklasse stecken, und dann die alte Klasse davon ableiten.

Beispiel analog zu den Funktionen von j0hnwayn3:

Code:
class Base
{
public:
    Base(std::string, std::string, std:string, std::string, bool);
protected:
    ~Base();
};

class Orig : public Base
{
public:
    Orig(std::string, std::string, std:string, std::string);
    Orig(std::string, std::string, bool);
    Orig(std::string, std::string, std:string, std::string, bool);
};

Code:
Orig::Orig(std::string s1, std::string s1, std:string s3, std::string s4) : Base(s1, s2, s3, s4, false) { }
Orig::Orig(std::string s1, std::string s2, bool b) : Base(s1, s2, "foo", "bar", b) { }
Orig::Orig(std::string s1, std::string s2, std:string s3, std::string s4, bool b) : Base(s1, s2, s3, s4, b) { }
 
L8N schrieb:
Mir fällt noch was ein, wenn auch etwas umständlicher :p Man könnte den bisherigen Code in eine neue Basisklasse stecken, und dann die alte Klasse davon ableiten.

Könnte man natürlich auch tun, aber extra eine Vererbungshierarchie einführen, nur um quasi delegating constructors zu bekommen, finde ich dann doch irgendwie ein bißchen overkill. :)
 
Edit: @antred Sind das in L8Ns Lösung nicht auch delegating constructors?

Edit2: Offenbar nicht... Wie nennt man dieses Konstrukt?


Naja ich hab wie gesagt kein C++11 zur Verfügung und deine Lösung enthält auch delegating constructors. Mit deren Hilfe würde man auch nicht zwei Klassen benötigen sondern könnte das ganze wie folgt schreiben

Code:
#include <iostream>

#define STRING1 "magic"
#define STRING2 "!!!"

class Base {
public:
    Base(const std::string& s1, const std::string& s2, const std::string& s3 = STRING1, const std::string& s4 = STRING2);
    Base(const std::string& s1, const std::string& s2, bool b);
    Base(const std::string& s1, const std::string& s2, const std::string& s3, const std::string& s4, bool b);
};

Base::Base(const std::string& s1, const std::string& s2, const std::string& s3, const std::string& s4)
    : Base(s1, s2, s3, s4, false) {}
Base::Base(const std::string& s1, const std::string& s2, bool b)
    : Base(s1, s2, STRING1, STRING2, b) {}
Base::Base(const std::string& s1, const std::string& s2, const std::string& s3, const std::string& s4, bool b) {
    std::cout << s1 << " "
        << s2 << " "
        << (b ? "cool " : "lame ") 
        << s3
        << s4
        << std::endl;
}

int main() {
    Base b1("don't do", "some");
    Base b2("do", "some", true);
    return 0;
}

So wäre mir das vermutlich am liebsten. Wenn ich jedoch mit -std=c++11 compiliere kracht es bei mir in include files auf die ich nicht im geringsten Einfluss habe.

Ich hab jetzt im Endeffekt zwei Konstruktoren die sich nur in der Reihenfolge der Parameter unterscheiden. Auf die init-Funktion hab ich verzichtet weil es sich jeweils nur um 5 Zeilen handelt. Das gefällt mir zwar absolut nicht, aber ich denke ich werde mich erstmal wichtigeren Dingen zuwenden und darauf zurückkommen wenn die Funktionalität gewährleistet ist.

Auf jeden Fall danke ich schonmal jedem der was beigetragen hat. Ich bin auch weiterhin für Vorschläge offen.
 
Zuletzt bearbeitet:
Zurück
Oben