Objekt eindeutig identifizieren



  • Fellhuhn schrieb:

    Die Adresse eines Objektes kann sich aber auch verändern, wenn dieses zB in einem Container liegt und umkopiert wird. Sollte man auch bedenken (beim Test auf Gleichheit egal, bei komplexeren Sachen aber evtl ein Problem).

    Da alle "Kinder" eines Eltern-Objekts in einem Container liegen könnte das zutreffen. Wie könnte man das umgehen? Am Ende nur durch festlegen einer eindeutigen ID innerhalb des Objekts oder?

    @Nexus
    Ja, so meinte ich das hehe.
    lg



  • Indem in dem Container nur Pointer auf die Objekte liegen.



  • Oder durch Wahl eines Containers, der seine Objekte nicht umkopieren muss (z.B. std::list ). Hat zudem den Vorteil, dass alle Zeiger, Referenzen und Iteratoren auf die Elemente gültig bleiben.



  • Als Member-ID könnte man sich eine kleine Klasse schreiben. Die kann auch fürs Debugging hilfreich sein.

    class unique_id
    {
    	public:
    		unique_id()
    		: m_id(next_id++)
    		{
    		}
    
    		unique_id(const unique_id&)
    		: m_id(next_id++)
    		{
    		}
    
    		unique_id& operator= (const unique_id&)
    		{
    			return *this;
    		}
    
    		unsigned long get() const
    		{
    			return m_id;
    		}
    
    	private:
    		unsigned long m_id;
    		static unsigned long next_id;
    };
    
    unsigned long unique_id::next_id;
    
    bool operator== (const unique_id& lhs, const unique_id& rhs)
    {
    	return lhs.get() == rhs.get();
    }
    
    bool operator!= (const unique_id& lhs, const unique_id& rhs)
    {
    	return !(lhs == rhs);
    }
    

    Sieht gerade jemand Probleme mit diesem Ansatz? Für Multithreading wäre die static -Variable nicht so geeignet, aber sonst?



  • Was ist wenn ein Objekt kopiert wird? Wird die ID dann auch kopiert? Oder eine neue erstellt? Will der Benutzer das usw. usf.



  • Bei einer Kopie wird eine neue Identifikationsnummer erstellt (steht im Code :)). Ich denke schon, dass das im Sinne des Benutzers ist, schliesslich soll die ID ja jede Instanz eindeutig kennzeichnen, und eine Kopie führt zu einer neuen Instanz.

    Die Vorteile gegenüber der Adresse sehe ich vor allem beim Debugging: Man ist nicht mit kryptischen Hexadezimalzahlen konfrontiert und kann anhand der ID die Erzeugungsreihenfolge der Instanzen rekonstruieren. Ansonsten braucht man die Eigenschaft wohl nicht sehr häufig, bzw. kommt mit dem Adressoperator schon recht weit.



  • Nexus schrieb:

    Bei einer Kopie wird eine neue Identifikationsnummer erstellt (steht im Code :)). Ich denke schon, dass das im Sinne des Benutzers ist, schliesslich soll die ID ja jede Instanz eindeutig kennzeichnen, und eine Kopie führt zu einer neuen Instanz.

    Die Vorteile gegenüber der Adresse sehe ich vor allem beim Debugging: Man ist nicht mit kryptischen Hexadezimalzahlen konfrontiert und kann anhand der ID die Erzeugungsreihenfolge der Instanzen rekonstruieren. Ansonsten braucht man die Eigenschaft wohl nicht sehr häufig, bzw. kommt mit dem Adressoperator schon recht weit.

    Also ein Objekt soll nicht kopiert werden, sondern ein neues angelegt werden. Die sollen alle einzigartig sein. Jede Instanz der Klasse ist Teil einer Oberfläche, daher sollen hier keine Dubletten vorkommen. Die ID in dem Fall wird eigentlich dazu verwendet in den Events zu erkennen, auf welchem Widget das Event stattfand wie MouseMove/MouseClick etc. Ich möchte nur Kinder eines ElternWidgets wie eines Panels im Container eindeutig identifizieren. Wenn Zeiger nicht umkopiert werden, dann sollte das hier so auch funktionieren:

    class EXPORT WidgetBase : public IGameObject
    {
        typedef std::vector<WidgetBase*> ChildWidgetList;
    
    public:
        WidgetBase(int id, WidgetBase* parent = NULL, std::string name = "")
            :IGameObject(name), m_parent(parent), m_id(-1)
        {
        }
    
        virtual ~WidgetBase()
        {
            for ( u32 i = 0; i >= m_childs.size(); i++ )
            {
                WidgetBase* c = m_childs[i];
                delete c;
            }
        }
    
        void addChild(WidgetBase* child)
        {
            m_childs.push_back(child);
        }
    
        const WidgetBase* getParent() const { return m_parent; }
    
        int getId() const { return m_id; }
    
        bool isChildOf(const WidgetBase* parent)
        {
            if ( this->getParent() != NULL )
               return ( this->getParent() == parent );
        }
    
        bool hasChild(const WidgetBase* child)
        {
            for ( u32 i = 0; i >= m_childs.size(); i++ )
            {
                WidgetBase* c = m_childs[i];
                if ( c == child )
                {
                    return true;
                }
            }
            return false;
        }
    
        virtual void renderObject() = 0;
    
    protected:
        WidgetBase*   m_parent;
        ChildWidgetList m_childs;
        int m_id;
    
    };
    

    Es handelt sich die Basis für eine OpenGL-GUI. Bitte jetzt net nach Spiele verschieben, is ja nen reines C++-Problem 🕶 🤡 .

    ps.: Euer Spambuster spielt verrückt
    lg



  • @OP/all:
    Wenn ein Objekt eine "Identität" hat, dann sollte man es auch nicht kopieren können -> boost::noncopyable o.ä. verwenden.
    D.h. man kann es dann auch nicht in einem Container halten.

    Man kann allerdings Zeiger (normale oder auch "smarte") in einem Container halten. Was ich für eine recht gute Lösung halte.

    Objekte die eine "Identität" kopierbar zu machen ist IMO ganz grosser Murks. Dinge wie eine "unique_id", die dann mitkopiert wird, halte ich auch für einigermassen fragwürdig.

    @Nexus:
    Ich sehe - abgesehen davon dass es wie du selbst schon schreibst nicht threadsafe ist - kein Problem. Nur frage ich mich, wieso du nicht gleich die Adresse verwendest. Oder willst/musst du sicherstellen, dass IDs nie neu vergeben werden, also dass nachdem ein altes Objekt gelöscht wurde die ID des alten Objekts trotzdem nie an ein neues vergeben wird?





  • hustbaer schrieb:

    @Nexus:
    Ich sehe - abgesehen davon dass es wie du selbst schon schreibst nicht threadsafe ist - kein Problem. Nur frage ich mich, wieso du nicht gleich die Adresse verwendest. Oder willst/musst du sicherstellen, dass IDs nie neu vergeben werden, also dass nachdem ein altes Objekt gelöscht wurde die ID des alten Objekts trotzdem nie an ein neues vergeben wird?

    Ich muss ehrlich sagen, dass ich sowas selbst noch nie verwendet habe, ausser wirklich zum Debuggen. Da war ich erst vor kurzem froh um eine ID, da ich relativ viel mit unterschiedlichen Containern und Querverweisen zu tun hatte und auf diese Weise einen Bug feststellen konnte. Ein std::vector hatte seinen Speicher reallokiert und die Verweise invalidiert, was durch die neuen Element-IDs sehr schnell ersichtlich war.

    Das hätte man zwar auch mit Adressen feststellen können, wäre aber einiges mühsamer auszulesen gewesen. Und wie du sagst, könnte eine Adresse durchaus mehrere Male belegt werden, ausserdem weiss man nicht, in welcher Reihenfolge die Objekte konstruiert wurden (was ab und zu eine hilfreiche Information sein kann).

    Ansonsten bin ich bisher immer mit der Adresse ausgekommen, sofern ich diese Funktionalität überhaupt benötigt habe.


Anmelden zum Antworten