C++ Lokale Objekte zerstören, die nicht im Heap stehen

Tockra schrieb:
Es gibt eigentlich keinen speziellen Grund warum ich den Heap nicht benutzen sollte. Der Heap bringt nur überall Probleme mit sich welche man lösen muss (Speicherleaks). Das schreckt mich immer etwas vor der Benutzung ab, aber ich denke hier wird das legitim sein.

Das ist eigentlich auch so. Wenn es keinen guten Grund gibt, den Heap zu nutzen, sollte man es auch nicht tun. Aber davon abgesehen, was ist denn nun eigentlich der konkrete Anwendungsfall, für den die Lebensdauer eines Objektes einschränken möchtest?
 
Achso, der Fall war eigentlich dass ich eine Klasse habe, die dazu da sein sollte UDP Packete zu empfangen, in welchen Audio Packete übertragen werden.
Der Server soll allerdings nur bereit zum empfangen sein, wenn der Client vorher über die Controllverbindung das Kommando geschickt hat, dass er jetzt vorhaben wird UDP Packete zu schicken und soll dann das Empfangen der Packete abbrechen, wenn der Client sagt, dass er nicht mehr senden möchte.
Ich habe mich aber von dieser Idee entfernt, da der Client den UDP Port kennen soll, bevor er die Controllverbindung aufbaut. Damit das alles funktioniert muss der Receiver Socket gleichzeitig mit dem Controllverbindungssocket geöffnet werden und ich teile dem Client dann beide Ports mit.
 
Ich habe mal eine kleine Test-Klasse geschrieben, die, wie NullPtr vorgeschlagen hat, Debugausgaben auf die Konsole gibt. Damit kannst du ja mal ein weniger herumexperimentieren.

Code:
#include <iostream>
#include <cstddef>
#include <utility>

class Test
{
public:
	Test() : m_charArray( nullptr ), m_sizeOfArray( 0 )
	{
		std::cout << "[Test::default c'tor] Instance at 0x" << std::hex << this << std::dec << " default-constructed.\n";
	}

	// NOTE: charArray must be a null pointer or a pointer to a heap-allocated array because Test's destructor will attempt to delete [] it!
	Test( char* charArray, std::size_t sizeOfArray ) : m_charArray( charArray ), m_sizeOfArray( sizeOfArray )
	{
		std::cout << "[Test::parametrizable c'tor] Instance at 0x" << std::hex << this << std::dec << " constructed with explicit values.\n";
	}

	Test( const Test& other ) : m_charArray( new char[ other.m_sizeOfArray ] ), m_sizeOfArray( other.m_sizeOfArray )
	{
		std::cout << "[Test::copy c'tor] Instance at 0x" << std::hex << this << " copy-constructed from instance 0x" << &other << std::dec << ".\n";

		for ( std::size_t i = 0; i < m_sizeOfArray; ++i )
		{
			m_charArray[ i ] = other.m_charArray[ i ];
		}
	}

	Test( Test&& other ) : m_charArray( other.m_charArray ), m_sizeOfArray( other.m_sizeOfArray )
	{
		std::cout << "[Test::move c'tor] Instance at 0x" << std::hex << this << " move-constructed from instance 0x" << &other << std::dec << ".\n";

		// Steal resources from other instance.
		other.m_charArray = nullptr;
		other.m_sizeOfArray = 0;
	}

	~Test()
	{
		std::cout << "[Test::d'tor] Instance at 0x" << std::hex << this << " is being discarded.\n";

		delete [] m_charArray;
	}

	Test& operator = ( const Test& rightHandSide )
	{
		std::cout << "[Test::copy assignment operator] Instance at 0x" << std::hex << this << " is being copy-assigned from instance 0x" << &rightHandSide << std::dec << ".\n";

		// prevent self-assignment
		if ( this == &rightHandSide )
		{
			return *this;
		}

		delete [] m_charArray;
		m_charArray = nullptr;
		m_sizeOfArray = rightHandSide.m_sizeOfArray;

		if ( rightHandSide.m_sizeOfArray == 0 )
		{
			// Other instance is empty. Nothing to copy.
			return *this;
		}

		m_charArray = new char[ rightHandSide.m_sizeOfArray ];

		for ( std::size_t i = 0; i < m_sizeOfArray; ++i )
		{
			m_charArray[ i ] = rightHandSide.m_charArray[ i ];
		}

		return *this;
	}

	Test& operator = ( Test&& rightHandSide )
	{
		std::cout << "[Test::move assignment operator] Instance at 0x" << std::hex << this << " is being move-assigned from instance 0x" << &rightHandSide << std::dec << ".\n";

		// prevent self-assignment
		if ( this == &rightHandSide )
		{
			return *this;
		}

		// Steal resources from other instance and give it ours (it will shortly be destroyed anyway).
		std::swap( m_charArray, rightHandSide.m_charArray );
		std::swap( m_sizeOfArray, rightHandSide.m_sizeOfArray );

		return *this;
	}

private:
	char* m_charArray;
	std::size_t m_sizeOfArray;
};

int main()
{
	Test a( new char[ 56 ], 56 );
	
	a = Test( new char[ 200 ], 200 );
}
 
Tockra schrieb:
Naja aber ich dachte Objekte werden gelöscht, sobald diese nicht mehr erreichbar sind (außer die im Heap). Wie lösche ich dann a ?

Du solltest dich unbedingt mit der Speicherverwaltung von C++ beschaeftigen. Gute Buecher findest du im C++ Forum.
- In deinem Anfangsbeispiel wird a nicht per Destruktor zerstoert, es wird zweimal neu zugewiesen. Erst nach Verlassen der Funktion wird der Destruktor aufgerufen.
- Benutze Smart-Pointer (unique_ptr/shared_ptr) und zum erzeugen make_unique/make_shared, dann brauchst du kein new/delete mehr (eine Fehlerquelle weniger!) und bist Exception-sicher.
- Stackobjekte kannst du vorher loeschen, indem du sie in einen eigenen Scope {} setzt.
Code:
{
   AudioInput a; // Konstruktion, alternativ a(...);
   /* code ... */
   a = nullptr; // Du meinst nullptr anstatt null?!?
} // beim verlassen des Scopes wird der Destruktor von a aufgerufen
 
Hi,
Du tust Dir ziemlich schwer dabei, weil C++ per Design versucht, genau das zu verhindern.
C++ setzt auf das RAII-Prinzip; d.h. die Freigabe von Ressourcen sollte möglichst nachvollziehbar elegant automatisiert geschehen. Siehe dazu auch den Wikipedia-Artikel zu RAII.
 
Ich arbeite zwar mit C aber sollte auch hier so sein, dort gilt:

Wird eine Funktion verlassen, dann werden die Daten abgeräumt, außer man hat es in den Heap geschrieben (malloc). Man brauch solche non-heap Dinger nicht manuell freen. Es gibt den Heap (permanent) und den Stack (temporär). Beide sind durch freien Speicher getrennt und wachsen sich entgegen, wächst eines zu sehr an und kollidiert mit dem anderen, gibt es einen StackOverflow etc. Passiert Anfängern oft bei tiefen Rekursionen, da wird für jeden Schritt etwas auf den Stack gelegt.

Liefert die Funktion einen Pointer auf so ein Objekt innerhalb der Funktion, welches nicht in den Heap geschoben wurde, wird es dennoch abgeräumt, der Pointer zeigt dann in einen undefinierten Bereich.

Java/Ruby usw. haben ja den Garbage Collector, der räumt alles weg was keine Referenz mehr hat.
D.h. keine Variable mehr die auf das Objekt zeigt = Objekt weg. In C wäre dies ein Memory Leak.
Wegen solchen Geschichten wird Anfängern Java empfohlen, da müssen sie nicht nicht um technischen Kram kümmern.
 
Zuletzt bearbeitet:
Kanibal schrieb:
Hi,
Du tust Dir ziemlich schwer dabei, weil C++ per Design versucht, genau das zu verhindern.
C++ setzt auf das RAII-Prinzip; d.h. die Freigabe von Ressourcen sollte möglichst nachvollziehbar elegant automatisiert geschehen. Siehe dazu auch den Wikipedia-Artikel zu RAII.

Tockra schrieb:
Der Fairness halber sollten wir es semiautomatisch nennen.

Nein, das RAII-Prinzip funktioniert immer, insbesondere dann wenn Exceptions geworfen werden. Welchen Sinn sollte es sonst auch haben?!? Ressourcen werden immer nur mit dem Destruktor abgeraeumt. Wie Kanibal bereits sagte, versuchst du dieses Prinzip auszuhebeln. Mit einem GarbageCollector (wie in Java, gibts auch fuer C++) kannst du es verschleiern, oder du nimmst Smart-Pointer/Stackobjekte und machst es richtig, dann funktionierts auch.
 
Zurück
Oben