2016-05-06 13 views
2

Ich mache ein kleines Tower Defense Spiel, um C++ zu üben. Ich kenne andere Sprachen, habe aber in C++ wenig wirkliche Entwicklung gemacht. In diesem Spiel, das ich mehrere Gebäude haben werde, so habe ich eine Klasse wie folgt:Kann ein std :: function member auf andere Mitglieder zugreifen?

class Building 
{ 
private: 
    sf::Sprite& sprite; 
    float timer = 0; 
    void update(float delta); 
}; 

Jetzt werde ich viele verschiedene Gebäude haben, aber sie sollten alle die gleichen Mitglieder. Nun, in Java würde ich eine Unterklasse für jeden anderen Gebäudetyp machen (müssen), bu dachte ich, kann ich void update(float delta); zu std::function<void(float)> update; ändern und Fabrikfunktionen haben, um die verschiedenen Gebäude zu schaffen? Was ich mir vorstellen, ist so etwas wie dieses:

In building.h:

class Building 
{ 
private: 
    sf::Sprite& sprite; 
    float timer = 0; 
    std::function<void(float)> update; 
}; 

Und in building.cpp

Building getBuildingA() 
{ 
    Building b; 
    b.sprite = /* ... */ 
    b.update = [](float delta) { 
     std::cout << timer++ << std::endl; 
    }; 
    return b; 
} 

Aber natürlich kann ich nicht wirklich die variable Timer zugreifen dort. Kann ich etwas Ähnliches tun?

+1

Durch die Aufnahme 'dieses'? – skypjack

+0

Sie können eine Referenz nicht nach einer solchen Zuweisung initialisieren. Es muss in einer Initialisierungsliste initialisiert werden. – kfsone

+0

Sie können das Datenschutzproblem umgehen, indem Sie getBuildingA zu einer statischen Elementfunktion von Building machen. – kfsone

Antwort

2

Sie können die Lambda-Erfassung this vornehmen. Eine bessere Lösung ist wahrscheinlich so etwas wie

class Building 
{ 
private: 
    sf::Sprite& sprite; 
    float timer = 0; 
    std::function<void(Building*, float)> update_impl; 
    void update(float x) { update_impl(this, x); } 
}; 

Aber wirklich, auf der Grundlage dieses Fragment des Designs sieht es aus wie Sie wirklich sollten Subklassen und virtuelle Funktionen werden verwendet; das ist die Art von Sache, die sie sind gemeint für.

Die typische Anwendung von function ist, wenn Sie Funktionsobjekte herum - z. das Java-Paradigma des Schreibens einer Interface-Klasse durch eine einzige Methode zu ersetzen, insbesondere in Einstellungen, in denen Sie lieber nur Funktionen oder Lambdas als ganze Objekte schreiben.

Für Objekt Polymorphismus, die wichtigste Alternative zu Subclassing und virtuelle Funktionen ist Templates und generische Programmierung verwenden - obwohl das ist vor allem für die, wenn Sie Polymorphismus müssen bei kompilieren Zeit passieren. (was, ich stelle mir vor, ist ungeeignet für Ihre Umstände)

+0

Gute Antwort, danke. Ich sehe deinen Punkt auf Unterklassen. Es gibt andere Probleme auch hier mit meinem Design, aber es ist in einem Versuch zu tun, was mit Lambdas und dergleichen getan werden kann. – Istarnion

+0

Der Vollständigkeit halber kann ich mir auch Designs vorstellen, bei denen eine "Funktion" ebenfalls angebracht ist; Ich habe hier nicht das vollständige Bild, so dass ein Teil meines Rates auf fundiertem Raten beruht. – Hurkyl

+0

Ah, ich denke, dass ich mit "Funktion" experimentieren will, ist ein relevanter Teil des kompletten Bildes, das mir fehlt. :) – Hurkyl

2

Sie können einfach erfassen this.
Als Beispiel:

struct S { 
    auto foo() { 
     return [this](){ this->bar(); }; 
    } 

    void bar() { } 
}; 

int main() { 
    S s; 
    s.foo()(); 
} 

Wenn Sie C++ 14 verwenden, können Sie stattdessen eine Initialisiererliste verwenden.
Als Beispiel:

#include<cassert> 

struct S { 
    auto foo() { 
     return [timer = timer](){ 
      assert(timer == 42); 
     }; 
    } 

    int timer{42}; 
}; 

int main() { 
    S s{}; 
    s.foo()(); 
} 

Auf diese Weise erhalten Sie eine Kopie initialisierte Variable Timer innerhalb des Körpers der Lambda-Funktion verwendet werden genannt bekommen.

0

Sie können das Datenschutzproblem umgehen, indem Sie eine statische Member-Funktion von Building verwenden, um die Factory-Klasse bereitzustellen.

Ein zweites Problem, mit dem Sie konfrontiert werden, besteht darin, dass Sie ein Gebäude nach Wert zurückgeben, so dass Sie die endgültige Adresse des Objekts, das Sie zurückgeben, nicht kennen. Sie können dies umgehen, indem Sie Ihre Funktion "update()" als Parameter übergeben.

#include <iostream> 
#include <functional> 

class Building { 
    // C++ classes are private by default 
    int timer_; 
    std::function<void(Building*)> update_; 

public: 
    void update() { 
     update_(this); 
    } 

    static Building getBuildingA() { 
     Building b; 
     b.timer_ = 42; 
     b.update_ = [](Building* b) { 
      b->timer_++; 
      std::cout << b->timer_ << '\n'; 
     }; 
    } 
}; 

int main() { 
    Building b = Building::getBuildingA(); 
    b.update(); 
    return 0; 
} 

Arbeits Demo: http://ideone.com/1Jy6zI

Aber auch darüber im Klaren sein, dass Sie zuweisen können nicht eine Referenz, es zu initialisieren, Sie müssen es initialisieren. Beachten Sie auch, dass C++ standardmäßig nicht gezählt wird. Daher kann Ihre Referenz auf das Sprite leicht zu einer Referenz werden.

Sie könnten eine std::shared_ptr anstelle einer Referenz betrachten.

Verwandte Themen