Frage zu Objektarray



  • Hmm, ich dachte das währe eindeutig. Ich möchte in dem Apfel-Array den ersten freien Platz finden, wo noch kein Apfel gespeichert wurde. Da steht bei mir der Kommentar im Quelltext, dass das so nicht funktioniert. Ich brauche irgendeine Möglichkeit oder Idee, wie ich das erreichen kann.



  • Pffft.

    Trille schrieb:

    Hmm, ich dachte das währe [sic] eindeutig.

    Nein.

    Trille schrieb:

    Ich möchte in dem Apfel-Array den ersten freien Platz finden, wo noch kein Apfel gespeichert wurde.

    Das ist das mehr oder weniger einzige was du konkret gesagt hast. Aber was meinst du überhaupt mit "freier Platz"?

    Trille schrieb:

    Da steht bei mir der Kommentar im Quelltext, dass das so nicht funktioniert.

    Oh, es funktioniert nicht? Stimmt, du hast recht, da steht's ja: "// geht irgendwie nicht". Wahnsinnig hilfreich, der Kommentar, wirklich.

    Trille schrieb:

    Ich brauche irgendeine Möglichkeit oder Idee, wie ich das erreichen kann.

    Gut, schau her:

    #include <vector>
    
    typedef std::vector<apfel> korb;
    
    void add_apfel( korb& k, int durchmesser, int gueteklasse)
    {
      k.push_back(apfel(durchmesser, gueteklasse));
    }
    

    Bringt dich das nicht weiter, siehe oben:

    <a href= schrieb:

    finix">Leg dein konkretes Problem dar oder adaptiere es mit Äpfeln, Orangen, was auch immer. Aber ohne Kontext kann man dir schlecht helfen.



  • aepfel = new Apfel[maximaleApfelAnzahl];
    

    Hier legst du N Äpfel an, die hier auch direkt mit dem Standard-Konstruktor initialisiert werden.

    aepfel[i] = NULL;       //geht irgendwie nicht
    

    Hier versuchst du, einem Objekt Apfel den Wert 0 zuzuweisen, klar geht das nicht. (Es sei denn es gäb den passenden Zuweisungsoperator bla bla bla)

    Ich denke, das was du suchst, ist ein Array von Zeigern auf Äpfel.
    Das sieht dann so aus:

    Apfel **aepfel;
    

    (Zeiger auf Zeiger, da Zeiger implizit als Array gehandelt werden)

    Am besten wäre aber, du benutzt gleich einen Container der Standardbibliothek (std::vector würde sich anbieten)



  • @ Trille:
    Du kommst von Java oder?
    In C++ erstellst du kein Array aus Referenzen, die du dann erstmal mit NULL initialisierst.
    Sondern das Array enthält sobald ihm mit new Speicher zugewiesen hast, auch Objekte.
    Ein Container aus der C++-Standardbibliothek würde dir whs helfen, oder du löst es eben per "Apfel **aepfel" - beachte auch, dass du unter C++ jeden Speicher, den du dynamisch per new allozierst auch wieder freigeben musst. Nochmal mein Verweis an die Standardbibliothek, die nimmt dir in dem Fall diese Aufgaben ab.



  • Richtig, ich komme von Java, es ist alles noch etwas ungewohnt,aber so langsam komme ich dahinter. Sicherlich sind viele Fragen noch stupide, aber mit der Zeit lernt man dazu. Die Pointer usw. sind noch ziemlich schwer.

    Ich danke erstmal für die Tipps, das klappt soweit auch ganz gut. Jetzt habe ich noch zwei Probleme. Ich poste jetzt mal die ganze Klasse.
    Header

    class ServerManager
    {
    
    public:
    	ServerManager();
    	~ServerManager();
    	void AddServer(int port, void (*funct)(SOCKET *sock, char* buffer));	
    	HANDLE StartServer(int port);
    	Server& GetServer(int port);
    
    private:
    
    	int maxClients;
    	std::vector<Server> servers;
    };
    
    DWORD WINAPI ThreadFunc(LPVOID Data);
    
    ServerManager::ServerManager()
    {
    
    }
    
    ServerManager::~ServerManager()
    {
    	std::vector<Server>::iterator iter;
    	for(iter = servers.begin(); iter != servers.end(); iter++)
        {
    		//Server x = ((Server)(*iter));  #3
    		//delete &x;
        }
    }
    
    void ServerManager::AddServer(int port, void (*funct)(SOCKET *, char *))
    {
    	Server *server = new Server(port, funct);
    	servers.push_back(*server);	
    }
    
    Server& ServerManager::GetServer(int port)
    {
    	std::vector<Server>::iterator iter;
    	for(iter = servers.begin(); iter != servers.end(); iter++)
        {
    		if( ((Server)(*iter)).getPort() == port)
    		{
    			return ((Server)*iter);
    		}
        }
    	return (servers.at(0));
    }
    
    HANDLE ServerManager::StartServer(int port)
    {
    	HANDLE hThread; 
    	DWORD dwThreadID; 
    	Server server = GetServer(port);
            printf("%i\n", server.getPort());       //#1
     	hThread = CreateThread( NULL, 0, ThreadFunc, &server, 0, &dwThreadID);
    	return hThread;
    }
    
    DWORD WINAPI ThreadFunc(LPVOID Data) 
    { 
    	printf("%i\n",((Server*)Data)->getPort());     //#2
    	((Server*)Data)->run();
    	return(0); 
    }
    

    Eine kurze Beschreibung:
    Die Klasse ist dazu gedacht, mehrere Server gleichzeitig zu verwalten. Mit addServer werden die Server hinzugefügt. getServer liefert mit den Server, der auf einem bestimmten Port läuft (ist eindeutig). StartServer startet den angegebenen Server (erstellt einen eigenen Thread).

    Jetzt brennts noch an 3 Stellen (Markiert mit #1, #2 und #3)

    Ich rufe das ganze folgendermassen auf:

    ServerManager *x = new ServerManager;
    	x->AddServer(90, NULL);
    	x->AddServer(81, NULL);
    	hThread = x->StartServer(81);
    

    Das bedeutet, dass die Ausgabe bei #1 und #2jeweils 81 sein müsste. Es kommt jedoch irgendwas mit -833243454 bei #1 und 1 bei #2 heraus. Was mache ich falsch?

    Wie kann ich im Destruktor alle erstellten Server wieder löschen? (#3)

    Vielen Dank schonmal im Vorraus.



  • ServerManager::ServerManager()
    {
    
    }
    
    ServerManager::~ServerManager()
    {
     //Der Destruktor übernimmt das aufräumen, da du Instanzen von Server in dem vector hast, und keine Pointer.
    }
    
    void ServerManager::AddServer(int port, void (*funct)(SOCKET *, char *))
    {
        Server server = Server(port, funct);
        servers.push_back(server);    
    }
    
    Server& ServerManager::GetServer(int port)
    {
        std::vector<Server>::iterator iter;
        for(iter = servers.begin(); iter != servers.end(); iter++)
        {
            if(iter->GetPort() == port)
            {
                return *iter;
            }
        }
        return (servers.at(0)); //Bist du dir sicher, dass auch ein Server im Vector existiert?
    }
    
    HANDLE ServerManager::StartServer(int port)
    {
        HANDLE hThread; 
        DWORD dwThreadID; 
        Server server = GetServer(port);
            std::cout << server.getPort() << std::endl       //C++ ;)
         hThread = CreateThread( NULL, 0, ThreadFunc, &server, 0, &dwThreadID); //Vorsicht, der Server liegt nur auf dem Stack und wird gelöscht wenn der Scope verlassen wird.
        return hThread;
    }
    
    DWORD WINAPI ThreadFunc(LPVOID Data) 
    { 
        std::cout << reinterpret_cast<Server*>(Data)->getPort() << std::endl; 
        reinterpret_cast<Server*>(Data)->run();
        return(0); 
    }
    

    So oder so ähnlich müsste es stimmen, allerdings musst du dir das mit dem Thread-Aufruf nochmal überdenken.



  • Vielen Dank,ich habe den Vector jetzt so umgeschrieben, dass er nicht mehr den Server an sich hält, sondern die Pointer darauf also

    vector<Server*>

    Dann funktionierts auch im Destruktor per Iterator. Ist das ganze jetzt auch noch kritisch hinsichtlich des Threads?



  • Also ... jetzt nochmal von vorne ... wir sind in C++, d.h. kann man wunderschön Vererbung nutzen! Nun legen wir uns also eine Basisklasse Server an, die wir dann immer ableiten können um die einzellnen Datenverarbeitungen anzupassen!

    class Server
    {
    public:
    	Server(std::size_t port) : m_socket(0), m_port(port) {}
    	Server(::SOCKET const& s) : m_socket(s) {}
    
    public:
    	virtual void handle_message(char*) {}
    	std::size_t const& get_port() const { return m_port; }
    	virtual void run() { /* ... */ }
    
    private:
    	::SOCKET	m_socket;
    	std::size_t	m_port;
    };
    

    Die Klasse ist jetzt nur einmal grob geschrieben, natürlich musst du sie deinen wünschen gemäß anpassen. handle_message entspricht deinem Funktionszeiger, den du im Konstruktor übergeben hast. Funktionszeiger sind aus C-Zeiten ... wenn du sowas haben willst, dann nur auf Memberfunktionen ^^ und das ist etw. komplizierter. Wenn du jetzt also die Verarbeitung von deinen Daten umändern willst, legst du dir einen Klasse an, die vom Typen Server erbt und gibst bei add_server als Template-Parameter halt den Namen deiner neuen Klasse an.
    Die Klasse ServerManager optimiert und für Vererbung angepasst:

    class ServerManager
    {
    public:
    	~ServerManager() { for (std::vector<Server*>::iterator it(m_servers.begin()); it != m_servers.end(); ++it) delete (*it); }
    public:
    	template<typename T>
    	void add_server(std::size_t port) { m_servers.push_back(new T(port)); }
        ::HANDLE start_server(std::size_t);
        Server& get_server(std::size_t);
        Server const& get_server(std::size_t) const;
    
    private:
    	static DWORD WINAPI fnThread(LPVOID);
    
    private:
        std::size_t				m_maxclient;
        std::vector<Server*>	m_servers;
    };
    
    Server& ServerManager::get_server(std::size_t port)
    {
        for(std::vector<Server*>::iterator it = m_servers.begin(); it != m_servers.end(); ++it)
            if ((*it)->get_port() == port) return *(*it);
        throw std::runtime_error("server not found");
    }
    
    Server const& ServerManager::get_server(std::size_t port) const
    {
        for (std::vector<Server*>::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it)
            if ((*it)->get_port() == port) return *(*it);
        throw std::runtime_error("server not found");
    }
    
    ::HANDLE ServerManager::start_server(std::size_t port)
    {
        if (m_servers.size() >= m_maxclient) throw std::out_of_range("max client");
    
    	Server& server = get_server(port);
        std::clog << "Server an Port " << server.get_port() << " erstellen." << std::endl;
        DWORD dwThreadID;
    	return ::CreateThread(NULL, 0, &ServerManager::fnThread, &server, 0, &dwThreadID);
    }
    
    DWORD WINAPI ServerManager::fnThread(LPVOID data)
    {
        Server* pInstance = static_cast<Server*>(data);
        std::clog << "Server an Port " << pInstance->get_port() << " starten." << std::endl;
        pInstance->run();
    
        return 0;
    }
    

    soo 🙂 So sieht die Klasse doch schon viel besser aus 🙂



  • Kannst du mir bitte noch kurz erklären, was diese Methode macht und was der Unterschied zu der anderen getServer-Methode ist?

    Server const& ServerManager::get_server(std::size_t port) const
    {
        for (std::vector<Server*>::const_iterator it = m_servers.begin(); it != m_servers.end(); ++it)
            if ((*it)->get_port() == port) return *(*it);
        throw std::runtime_error("server not found");
    }
    


  • Hmm klar 🙂 Also ... im Unterschied zu der anderen Methode, gibt die Funktion eine Referenz eines konstanten Objektes zurück und die Funktion selbst ist auch konstant. Das bringt dir Performancevorteile ...

    void foo(ServerManager const& manager) { Server const& server = manager.get_server(1234); }
    

    ... So lange du an Server nichts veränderst, ist diese Methode klar zu bevorzugen 🙂

    std::vector<T>::const_iterator
    

    ist die Version eines

    std::vector<T>::iterator
    

    , die auch "konstant" ist ... guck dir einfach mal nen guten Text zu const-correctness an ^^

    Achja und zum Unterschied zu deinem Code wird am Ende eine Exception geworfen, wenn get_server fehlschlägt ... du gibst einfach den 1. besten Server zurück 😛


Anmelden zum Antworten