Frage zu virtual



  • Angenommen ich hab eine virtuelle Klasse Blub mit der virtuellen Methode "int blub()"

    Bei Java (angenommen Blub ist ein Interface) könnte ich jetzt innerhalb einer Methode sowas schreiben:

    Blub b = new Blub() {int blub() {return 42;}};
    

    Geht sowas ähnliches auch mit C++?



  • Meinst du sowas?

    class Blub
    {
    public:
    	virtual int blub( )
    	{
    		return 42;
    	}
    };
    

    Dann kannst so drauf zugreifen:

    Blub b;
    	int i = b.blub( );
    

    Edit: Du könntest einfach den this-Pointer nehmen wenn du auf das Objekt zugreifen möchtest.

    this->blub( );
    


  • Nein, das geht in der Form nicht. ABer wozu brauchst Du das? In Java implementiert man damit i.d.R. sowas wie Listener on the fly.
    In C++ hat man mit statischen Delegetes über bind und function oder sowas wie boost::signals andere Möglichkeiten, sowas zu realisieren (und ist damit oft sogar flexibler).
    Biepsiel:

    #include <functional>
    #include <iostream>
    
    struct action_class
    {
    	void do_action(std::function<int ()> const & f)  //will std::function anstatt interface
            { 
                std::cout << f() << std::endl; 
            }
    };
    
    int get_value(){ return 42; }
    int calc_from_value(int v){ return 2*v; }
    
    struct value_provider
    {
    	int get() const{ return 42; }
    };
    
    int main()
    {
    	action_class my_class;
    	my_class.do_action([](){ return 42; }); //lambda als implementierung
    	my_class.do_action(&get_value); //benutzt freie Funktion get_value
    	my_class.do_action(std::bind(&calc_from_value, 21)); //benutzt freie Funktion mit parameter
    	value_provider p;
    	my_class.do_action(std::bind(&value_provider::get, &p)); //benutzt member von value_provider
    }
    

    Die Variante mit dem Lambda ist dabei sehr nahe an dem dran, was Du willst.



  • Also eigentlich geht es um folgendes:
    Ich hab eine Funktion bayReg, die als Parameter eine Funktion der Signatur "T (*func) (const Matrix<T>*, const T*, const T*)" bekommen soll. Bisher hab ich tatsächlich diese Signatur in der Parameterliste stehen. Ich finde das aber recht unschön, bei Java würde ich ein Interface schreiben. Denn es kann ja einen Fall geben, dass man eine Funktion folgender Form braucht (mathematisch hingeschrieben):
    int -> (T (*func) (const Matrix<T>*, const T*, const T*))
    Dafür könnte man eine Klasse schreiben, die das Interface implementiert und beim Konstruktor dieser Klasse wird ein int-Wert übergeben.
    Falls man solch einen int-Parameter o.Ä. nicht braucht, kann man bei Java eine Instanz des Interface halt auch im Code innerhalb einer Methode schreiben und mich hatte jetzt interessiert, ob das bei C++ auch irgendwie geht. Sowas ist keinesfalls zwingend notwendig, da man immer eine Klasse schreiben kann, in der man die abstrakte Methode implementiert.

    Trotzdem danke für die Antworten.
    ich wusste gar nicht, dass es sowas wie lambda-Ausdrücke auch in C++ gibt, ich kannte die bisher nur aus Haskell^^



  • Tachyon hat doch bereits ganz viele Möglichkeiten genannt, du solltest schon erklären, was dir daran nicht gefällt.

    Abgesehen davon gibt es natürlich noch:

    template <typename Functor>
    void foo(Functor f)
    {
      f(a, b, c);
    }
    

    Ist natürlich nicht so schön, da die Sprache noch keine Concepts/Modules kennt :(, erfüllt aber seinen Zweck und dürfte im Zweifelsfall etwas schneller als std::function sein.



  • Ich hab noch eine Frage:

    Folgender Code, wird zwar kompiliert, Eclipse zeigt mir aber zwei Warnungen an:

    #ifndef BAYREGFUNC_H_
    #define BAYREGFUNC_H_
    
    #include "matrix.h"
    
    template<class T>
    class BayRegFunc {
    public:
    	virtual T func(const Matrix<T>*, const T*, const T*) const = 0;
    };
    
    template<class T>
    class BayReg_pNormal: public virtual BayRegFunc<T> {
    public:
    	BayReg_pNormal();
    	BayReg_pNormal(T sigma);
    	virtual T func(const Matrix<T>*, const T*, const T*) const;
    };
    
    #endif /* BAYREGFUNC_H_ */
    

    Warnungen:
    Zeile 7: Class '[C@683332' has virtual method 'func' but non-virtual destructor
    Zeile 13: Class '[C@f92b51' has virtual method 'func' but non-virtual destructor

    Muss, bzw. vielmehr sollte man immer einen Destruktor deklarieren, auch wenn man keinen braucht, da es nichts zu tun gibt? Oder was soll mir diese Warnung sagen?



  • Sehr schöne Warnung!
    Du solltest einen virtuellen Destruktor immer dann einführen, wenn du erwartest, dass Objekte einer abgeleiteten Klasse als Basisklasse gelöscht werden. Also so:

    class A
    {};
    
    class B : public A
    {};
    
    int main()
    {
      A* a = new B();
      delete a; // aua ohne virtuellen Destruktor
    }
    

    Die abgeleiteten Objekte müssen den virtuellen Destruktor aber nicht überschreiben, der wird ja vererbt. Eine Klasse mit virtuellen Methoden, bei denen dieses Verhalten nicht erwatet wird, ist natürlich nicht sonderlich sinnvoll. Daher die Warnung.



  • ok, thx.

    Ich hab jetzt noch einen Fehler, den ich nicht finde:

    Hier die Ausgabe vom Compiler:

    g++ -O0 -g3 -Wall -c -fmessage-length=0 -o src\matrix.o ..\src\matrix.cpp
    In file included from ..\src\matrix.h:15,
                     from ..\src\matrix.cpp:10:
    ..\src\BayRegFunc.h:17: error: expected `,' or `...' before '<' token
    ..\src\BayRegFunc.h:17: error: ISO C++ forbids declaration of `Matrix' with no type
    ..\src\BayRegFunc.h:25: error: expected `,' or `...' before '<' token
    ..\src\BayRegFunc.h:25: error: ISO C++ forbids declaration of `Matrix' with no type
    ..\src\matrix.cpp: In member function `Matrix<T> Matrix<T>::bayReg(const T*, T*, double, double, double, long long int, int, int, const BayRegFunc<T>*) const [with T = double]':
    ..\src\matrix.cpp:2335:   instantiated from here
    ..\src\matrix.cpp:1906: error: no matching function for call to `BayRegFunc<double>::func(const Matrix<double>* const, const double*&, const double*) const'
    ..\src\BayRegFunc.h:17: note: candidates are: T BayRegFunc<T>::func(int) const [with T = double]
    ..\src\matrix.cpp:1926: error: `func' cannot be used as a function
    

    // BayRegFunc.h

    #include "matrix.h"
    
    template<class T>
    class BayRegFunc {
    public:
    	virtual ~BayRegFunc();
    	virtual T func(const Matrix<T>* a, const T* b, const T* x) const = 0;
    };
    
    template<class T>
    class BayReg_pNormal: public virtual BayRegFunc<T> {
    public:
    	BayReg_pNormal();
    	BayReg_pNormal(T sigma);
    	virtual T func(const Matrix<T>* a, const T* b, const T* x) const;
    //	static BayReg_pNormal<T> *pNormal;
    private:
    	T sigma_;
    };
    

    // BayRegFunc.cpp

    #include "BayRegFunc.h"
    
    template <class T>
    BayReg_pNormal<T>::BayReg_pNormal() {
    	sigma_ = 1;
    }
    
    template <class T>
    BayReg_pNormal<T>::BayReg_pNormal(T sigma) {
    	sigma_ = sigma;
    }
    
    template <class T>
    T BayReg_pNormal<T>::func(const Matrix <T> *a, const T* b, const T* x) const {
    	T sum = 0;
    	for (int i = 0; i < a->getDim1(); i++) {
    		T t = 0 - b[i];
    		for (int j = 0; j < a->getDim2(); j++) {
    			t += a->getEntry(i,j)*x[j];
    		}
    		sum += t*t;
    	}
    	sum = sum / (-2 * sigma_ * sigma_);
    	// TODO: parameter covarianz-matrix
    	return sum;
    }
    
    template class BayReg_pNormal<int>;
    template class BayReg_pNormal<long long>;
    template class BayReg_pNormal<float>;
    template class BayReg_pNormal<double>;
    

    // matrix.h (kleiner Ausschnitt)

    #include "BayRegFunc.h"
    
    template<class T>
    class Matrix {
    public:
    	Matrix <T> bayReg(const T* b, T* start, double sigma, double accR_min, double accR_max, long long anzahl, int burnIn, int leaveOut, const BayRegFunc<T> *func) const;
    };
    

    In der matrix.cpp wird diese Zeile aufgerufen:

    T ppre = func->func(this, b, (const T*) xpre);
    

    Noch eine Frage:
    Wenn ich ein Objekt, das zu der Oberklasse BayRegFunc passt, übergeben will, muss ich dann in der Parameterliste "BayRegFunc<T>*" schreiben oder geht das auch ohne Sternchen?



  • Kannst du die Zeilen markieren die der Compiler meint? Denn offensichtlich passen die Zeilen nicht zusammen, und so ist das etwas mühsam.

    Zu der Pointer frage: Polymorphie funktioniert auch über Referenzen. Nur value-types gehen logischerweise nicht, wenn du ein abgeleitetes Objekt in eine Basisklasse kopiert, ist es ein Objekt der Basisklasse. Also das hier geht:

    class A {};
    class B : public A {};
    
    void foo(A& a) {}
    
    int main()
    {
      B b;
      foo(b);
    }
    

    PS: Bist du dir sicher, dass du virtuell vererben willst? Weist du überhaupt, was das bedeutet?

    Edit:
    Du solltest außerdem C++ Casts (static_cast, reinterpret_cast, const_cast) den C Casts gegenüber bevorzugen.
    Weiter scheint mir "bayReg" ein starker Kandidat für eine freie Funktion (keine Memberfunktion) zu sein.



  • Du kannst Templates nicht in .h und .cpp aufteilen.

    Ansonsten noch mal: Benutze std::function:
    Matrix.h (muss nicht mehr BayRegFunc.h und somit keine cirkularen Includes mehr)

    #include <functional>
    
    template<class T>
    class Matrix 
    {	
    public:
        typedef std::function<T (Matrix<T> const *, T const *, T const *)> CallbackType;
        Matrix <T> bayReg(const T* b, T* start, double sigma, double accR_min, double accR_max, long long anzahl, int burnIn, int leaveOut, CallbackType const & func) const
        //                                                                                                                          Hier:	^^^^^^^^^^^^^^^^^^^^^^^
        {
            //Implementierung aus Matrix.cpp direkt hier im Header
        }
    };
    

    Anstatt

    T ppre = func->func(this, b, (const T*) xpre);
    

    dann

    T ppre = func(this, b, (const T*) xpre);
    

    BayReg_pNormal::func kann dann einfach eine freie Funktion sein:

    template <class T>
        T BayReg_pNormal(const Matrix <T> *a, const T* b, const T* x, T sigma) //freie funktion!
        {
             //...
        }
    
        //...
    
        Matrix<T> m;
        m.bayReg(/*....*/, std::bind(&BayReg_pNormal<T>, _1, _2, _3, 42)); //sigma ist 42
    


  • cooky451 schrieb:

    Kannst du die Zeilen markieren die der Compiler meint? Denn offensichtlich passen die Zeilen nicht zusammen, und so ist das etwas mühsam.

    Sry, "HIER" sind die relevanten Zeilen

    // BayRegFunc.h

    #include "matrix.h"
    
    template<class T>
    class BayRegFunc {
    public:
        virtual ~BayRegFunc();
        virtual T func(const Matrix<T>* a, const T* b, const T* x) const = 0; // == HIER ==
    };
    
    template<class T>
    class BayReg_pNormal: public virtual BayRegFunc<T> {
    public:
        BayReg_pNormal();
        BayReg_pNormal(T sigma);
        virtual T func(const Matrix<T>* a, const T* b, const T* x) const; // == HIER ==
    private:
        T sigma_;
    };
    

    cooky451 schrieb:

    PS: Bist du dir sicher, dass du virtuell vererben willst? Weist du überhaupt, was das bedeutet?

    Nein
    Wahrscheinlich nein. Bis vor kurzem dachte ich noch, dass virtuell gleich dem abstract von java ist...
    Vllt kannst du mir es nochmal genauer erklären oder mir eine entsprechenden Link posten, wo es erklärt wird.

    cooky451 schrieb:

    Du solltest außerdem C++ Casts (static_cast, reinterpret_cast, const_cast) den C Casts gegenüber bevorzugen.

    Warum sollte man das machen? Die C Casts scheien ja auch zu funktionieren.

    cooky451 schrieb:

    Weiter scheint mir "bayReg" ein starker Kandidat für eine freie Funktion (keine Memberfunktion) zu sein.

    Wann ist eine freie Funktion sinnvoll und wann nicht? Wirklich entscheidend sind die Parameter "T* b" und "long long anzahl", die anderen könnte man mit default-Werten belegen.

    Tachyon schrieb:

    Du kannst Templates nicht in .h und .cpp aufteilen.

    Aber bei der Matrix-Klasse hab ich das doch auch gemacht und bisher hat alles funktioniert.

    Tachyon schrieb:

    Ansonsten noch mal: Benutze std::function:
    Matrix.h (muss nicht mehr BayRegFunc.h und somit keine cirkularen Includes mehr)

    Ich werde mir die Sache mit std::function nochmal angucken. Aber eigentlich müsste das doch auch so gehen, wie ich es programmiert habe, oder?

    Ah, ich glaube, gerade wurde mir klar, was function und bind macht: Im Prinzip wird nur der 4. Parameter eingesetzt, die ersten 3 bleiben unbestimmt. Das ist auf jeden Fall keine schlechte Lösung. Sry, wenn ich sie zuerst abgelehnt habe^^

    Warum bei meiner Lösung der Fehler kommt, würde mich trotzdem intressieren

    EDIT:
    Ok, ich bin einen Schritt weiter (bei meiner Lösung). Hier der Anfang der matrix.h

    #ifndef MATRIX_H_
    #define MATRIX_H_
    
    #include <utility>
    #include <iostream>
    #include "Polynomial.h"
    #include "RandomMatrix.h"
    #include <fstream>
    #include <vector>
    
    template<class T>
    class Matrix;
    
    #include "BayRegFunc.h"
    

    Ich denke mal, man kann sich denken, dass das include der BayRegFunc.h vorher etwas höher gestanden hat.

    Bei Linken bekomme ich jetzt trotzdem noch Fehler:

    g++ -LC:\Program Files (x86)\lapackpp\lib -o Linear Algebra.exe src\vector.o src\matrix.o src\Useful.o src\SlowRM.o src\RandomMatrix.o src\Polynomial.o src\Linear Algebra.o src\IndrandRM.o src\BayRegFunc.o -llibblas32 -lliblapack32
    src\BayRegFunc.o: In function `ZTv0_n12_N14BayReg_pNormalIdED0Ev':
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.rdata$_ZTV10BayRegFuncIdE[vtable for BayRegFunc<double>]+0x8): undefined reference to `BayRegFunc<double>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.rdata$_ZTV10BayRegFuncIdE[vtable for BayRegFunc<double>]+0xc): undefined reference to `BayRegFunc<double>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.rdata$_ZTV10BayRegFuncIfE[vtable for BayRegFunc<float>]+0x8): undefined reference to `BayRegFunc<float>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.rdata$_ZTV10BayRegFuncIfE[vtable for BayRegFunc<float>]+0xc): undefined reference to `BayRegFunc<float>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.rdata$_ZTV10BayRegFuncIxE[vtable for BayRegFunc<long long>]+0x8): undefined reference to `BayRegFunc<long long>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.rdata$_ZTV10BayRegFuncIxE[vtable for BayRegFunc<long long>]+0xc): undefined reference to `BayRegFunc<long long>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.rdata$_ZTV10BayRegFuncIiE[vtable for BayRegFunc<int>]+0x8): undefined reference to `BayRegFunc<int>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.rdata$_ZTV10BayRegFuncIiE[vtable for BayRegFunc<int>]+0xc): undefined reference to `BayRegFunc<int>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.text$_ZN14BayReg_pNormalIiED1Ev[BayReg_pNormal<int>::~BayReg_pNormal()]+0x35): undefined reference to `BayRegFunc<int>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.text$_ZN14BayReg_pNormalIiED0Ev[BayReg_pNormal<int>::~BayReg_pNormal()]+0x35): undefined reference to `BayRegFunc<int>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.text$_ZN14BayReg_pNormalIxED1Ev[BayReg_pNormal<long long>::~BayReg_pNormal()]+0x35): undefined reference to `BayRegFunc<long long>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.text$_ZN14BayReg_pNormalIxED0Ev[BayReg_pNormal<long long>::~BayReg_pNormal()]+0x35): undefined reference to `BayRegFunc<long long>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.text$_ZN14BayReg_pNormalIfED1Ev[BayReg_pNormal<float>::~BayReg_pNormal()]+0x35): undefined reference to `BayRegFunc<float>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.text$_ZN14BayReg_pNormalIfED0Ev[BayReg_pNormal<float>::~BayReg_pNormal()]+0x35): undefined reference to `BayRegFunc<float>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.text$_ZN14BayReg_pNormalIdED1Ev[BayReg_pNormal<double>::~BayReg_pNormal()]+0x35): undefined reference to `BayRegFunc<double>::~BayRegFunc()'
    C:/Program Files/cpp-eclipse/Projekte/Linear Algebra/Debug/../src/BayRegFunc.cpp:(.text$_ZN14BayReg_pNormalIdED0Ev[BayReg_pNormal<double>::~BayReg_pNormal()]+0x35): undefined reference to `BayRegFunc<double>::~BayRegFunc()'
    


  • Wichtig ist zu wissen, dass du nicht virtuell vererben musst, damit virtual funktioniert. Das ist für spezielle Mehrfachvererbung, wie hier erklärt wird: http://de.wikibooks.org/wiki/C++-Programmierung:_Vererbung#Virtuelle_Vererbung

    cooky451 schrieb:

    Der aus C bekannte Cast birgt einige Unsicherheiten, da er selbst nichts über seine Absicht verrät. Soll heißen, alle Umwandlungen die man über Casts erreichen kann, funktionieren mit ihm.

    In C++ wurde diese Funktionalität aufgeteilt:

    static_cast: Dieser ist der "gute" cast. Er kann kein const entfernen und nicht zwischen unabhängigen Pointern konvertieren.

    reinterpret_cast: Der Holzhammer. Konvertiert Pointer/Referenzen unabhängiger Typen ineinander. (Oder Integrale Typen zu Pointer/Referenzen und bei Pointern umgekehrt.)

    const_cast: Kann Zeiger und Referenzen auf konstante Objekte in Zeiger oder Referenzen auf nicht konstante Objekte casten.

    Es vermeidet einfach Flüchtigkeitsfehler, ein static_cast kann nicht einfach ein int in einen Pointer casten.

    Scott Meyers schrieb:

    Prefer non-member non-friend functions to member functions.

    Ich stimme da nicht 100%ig überein, aber das hier ist definitiv ein Fall dafür. Implementiere nur Funktionen als Memberfunktionen, die auch wirklich etwas mit der Kapselung der Klasse zu tun haben. Rechnungen auf Matrizen etc. gehören nicht dazu, die können ja über operator[] oder operator() auf alles zugreifen, was sie brauchen.

    Zu den Templates:
    Es ist richtig, die vollständige Definition eines Templates muss bekannt sein, um es zu nutzen. Daher implementiert man Templates oft im Header.



  • thx für den Link. Der Unterschied war mir in der Tat nicht klar.

    Den Fehler beim Linken hab ich selbst gefunden.

    Ich hab noch eine allgemeine Frage zu templates:
    Angenommen ich schreibe eine template<class T>-Methode. Und möchte jetzt den Code für unterschiedliche T unterschiedlich implementieren. Z.B. soll für double die sqrt()-Funktion genutzt werden und für int das Quadrat berechnet werden. Ok, das ist jetzt total aus der Luft gegriffen, aber geht sowas prinzipiell?



  • Spezialisieren solltest du bei Template-Typen. (Google "template specialization") Bei Funktionen nimm einfach eine Überladung, die wird dann dem Template gegenüber bevorzugt.


Anmelden zum Antworten