Dynamische Array Container Implementierung



  • Hi,
    ich soll einen Dynamischen Array Container Implementieren und komme einfach net weiter 😞 . Bitte um Hilfe Tipp und Tricks sehr erwünscht.

    DynArray:

    #pragma once
    #pragma warning(disable:4996)
    
    #include <algorithm>
    
    template < typename T >
    class DynArray
    {
    	T* pfirst, *plast, *pend;
    public:
    	friend class TestDynArray;
    
    	/*	DynArrays default constructor
    		Constructs an empty DynArray, with no content and a size of zero.
    	*/
    	DynArray(void)
    	{
    		pfirst = plast = pend = NULL;    
    	}
    
    	/*	Repetitive sequence constructor
    		Initializes the DynArray with its content set to a repetition, n times, of copies 
    		of value. The constructed container has a size and capacity of n.
    	*/
    	DynArray( size_t n, const T& val=T() )
    	{
    		pfirst = new T[n]; // calls T() n times
    		plast = pend = pfirst+n;
    		std::fill( pfirst, plast, val );
    	}
    
    	/*	DynArray copy-constructor
    
    	*/
    	DynArray( const DynArray& d )
    	{
    		size_t _capacity = d.pend-d.pfirst;
    		pfirst = new T[_capacity];
    		plast  = std::copy( d.pfirst, d.pfirst+(d.plast-d.pfirst), pfirst );
    		pend   = pfirst + _capacity;
    	}
    
    	/*	DynArray destructor
    		Destructs the container object. This calls each of the contained element's 
    		destructors, and deallocates all the storage capacity allocated by the DynArray.
    	*/
    	~DynArray(void){
    		T typ;
    		for(unsigned int i=0;i<size();i++)
    		{
    			typ.~T();	
    		}
    		pfirst = plast = pend = NULL;
    		delete [] pfirst;
    		delete [] plast;
    		delete [] pend;	
    	}
    
    	/*	Return size of allocated storage capacity
    		Returns the size of the allocated storage space for the elements of the 
    		DynArray container.
    	*/
    	size_t capacity() const { return pend-pfirst; }
    
    	/*	Return size
    		Returns the number of elements in the DynArray container.
    		This is the number of actual objects held in the DynArray, 
    		which is not necessarily equal to its storage capacity. 
    	*/
    	size_t size() const { return plast-pfirst; }	
    
    	/*	Clear content
    		All the elements of the DynArray are dropped by setting size to zero.
    		Notice that no element destructor is called and no memory is deallocated
    	*/
    	void clear() { plast = pfirst; }
    
    	/* Add element at the end
    	   Adds a new element at the end of the DynArray, after its current last element. 
    	   The content of this new element is initialized to a copy of x.
    	   This effectively increases the DynArray size by one, which causes a reallocation 
    	   of the internal allocated storage if the DynArray size was equal to the DynArray
    	   capacity before the call.
    	   During reallocation the new storage capacity should be set to 
    		newCapacity = 1					if capacity()=0
    		newCapacity = capacity()*3/2+1	if capacity()>0
    	  */
    	void push_back( const T& val ){
    	size_t sz = size()+1;
    	size_t t = size();
    	T* pdest = new T[sz];
    	std::copy(pfirst,plast,pdest);
    	delete [] pfirst;
    	pfirst = pdest;
    	plast = pend = plast+sz;                //<---geht net :-(
    	std::fill(pfirst+t,plast,val);
    	*if(capacity() == 0){}
    	if(capacity() > 0){}*/
    	}
    
    	/*	Access element
    		Returns a reference to the element at position n in the DynArray container.
    	*/
    	T& operator[]( size_t i ) { return pfirst[i]; }
    	const T& operator[]( size_t i ) const { return pfirst[i]; }
    
    	/*	Request a change in capacity
    		Requests that the capacity of the allocated storage space for the elements of 
    		the DynArray container be at least enough to hold n elements.
    		Notice that the parameter n informs of a minimum, so the resulting capacity may be 
    		any capacity equal or larger than this.
    		A call to this function never affects the elements contained in the DynArray, 
    		nor the DynArray size
    	*/
    	void reserve ( size_t n )
    	{
    		if(pend<pfirst+n) 
    		{
    			T* ptmp = new T[n];	// calls n times T's standard contructor
    			plast = std::copy( pfirst, plast, ptmp );
    			delete [] pfirst; // calls capacity times T's destructor
    			pfirst = ptmp; 
    			pend = pfirst+n;
    		}
    	}
    
    	/*	Change size
    		Resizes the DynArray to contain sz elements.
    		If sz is smaller than the current DynArray size, the content is reduced to its 
    		first sz elements, the rest being dropped. If sz is greater than the current 
    		DynArray size, the content is expanded by inserting at the end as many copies 
    		of val as needed to reach a size of sz elements. This may cause a reallocation.
    		Notice that this function changes the actual content of the DynArray by inserting 
    		or erasing elements from the DynArray; it does not only change its storage capacity.
    	*/
    
    	void resize ( size_t sz, T val = T() ){
    
    																	//sz = 4
    		if (sz > size()){											//h  = 1
    																	//size() oder b = 1
    			size_t b= size();										// e = 2
    			T *pdest= new T[sz]; // calls T() n times				//neu int [2]
    			std::copy(pfirst,plast,pdest);							//pdest size() = 1
    			delete [] pfirst;										//delete [] pfirst ;pfirst = pdest
    			pfirst = pdest;
    			plast = pend = pfirst+sz;								
    			std::fill(pfirst+b,plast, val);                         
    		}
    		if (sz < size()){
    
    			T* pdest = new T[sz];
    			std::copy(pfirst,plast,pdest);
    			delete [] pfirst;
    			pfirst = pdest;
    			plast = pend = pfirst+sz;
    		}
    
    	}
    
    	/*	Copy DynArray content
    		Assigns a copy of DynArray d as the new content for the DynArray object.
    		The elements contained in the DynArray object before the call are dropped, and 
    		replaced by copies of those in DynArray x, if any. After a call to this member 
    		function, both the DynArray object and DynArray x will have the same size and 
    		compare equal to each other.
    		Return value *this
    	*/
    		DynArray& operator=( const DynArray &m ){ 
      if (this != &m)  //oder if (*this != &m) 
      { 
    	/*pfirst = m.pfirst;*/  //<----------warum geht das net 
      } 
      return *this; //Referenz auf das Objekt selbst zurückgeben 
    }
    
    };
    

    Test-Unit header:

    #pragma once
    
    class Any {
    	int val;
    	static int count;
    public:
    	Any() { val=0; ++count; }
    	Any( int val0 ) { val = val0; ++count; }
    	Any( const Any& a ) { val = a.val; ++count; }
    	~Any() { --count; }
    	static void clear(){count=0;}
    	static size_t getCount() { return count; }
    	bool operator==( const Any&  a ) const { return (val==a.val); }
    	bool operator!=( const Any&  a ) const { return (val!=a.val); }
    };
    
    class TestDynArray
    {
    public:
    	TestDynArray(void);
    	~TestDynArray(void);
    	void test_all();
    	void test_ctors(); // size()and capacity() are also tested
    	void test_dtor();
    	void test_clear();
    	void test_IndexOperator();
    	void test_assignment();
    	void test_push_back();
    	void test_reserve();
    	void test_resize();
    };
    

    Test-Unit cpp:

    #include <iostream>
    #include <iomanip>
    #include "DynArray.h"
    #include "TestDynArray.h"
    #include <vector>
    #pragma warning(disable : 4996)
    
    using namespace std;
    #define fieldWidth 55
    
    int Any::count = 0;
    
    TestDynArray::TestDynArray(void)
    {
    	cout << left;
    }
    
    TestDynArray::~TestDynArray(void)
    {
    }
    
    void TestDynArray::test_all()
    {
    	std::cout << "\n************** TEST class DynArray ************************\n";
    	test_ctors(); // size()and capacity() are also tested
    	test_dtor();
    	test_clear();
    	test_IndexOperator();
    	test_assignment();
    	/*test_push_back();*/
    	test_reserve();
    	test_resize();
    	/*test_eraseRange();*/
    	cout<<endl;
    }
    
    void TestDynArray::test_ctors() // size()and capacity() are also tested
    {
    	int cnt = Any::getCount();
    	bool pass = true;
    	DynArray<int> d;
    	if( d.size()!=0 ) pass=false;
    	if( d.capacity()!=0 ) pass=false;
    	cout<< setw(fieldWidth)  << "1  - DynArray()" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    
    	pass = true;
    	DynArray<int> d1(3,2);
    	if( d1.size()    !=3 ) pass=false;
    	if( d1.capacity()!=3 ) pass=false;
    	if( d1.pfirst[0] !=2 ) pass=false;
    	if( d1.pfirst[1] !=2 ) pass=false;
    	if( d1.pfirst[2] !=2 ) pass=false;
    	DynArray<Any> d2(3);
    	if( d2.size()    !=3 ) pass=false;
    	if( d2.capacity()!=3 ) pass=false;
    	if( d2.pfirst[0] != Any() ) pass=false;
    	if( d2.pfirst[1] != Any() ) pass=false;
    	if( d2.pfirst[2] != Any() ) pass=false;
    	cout<< setw(fieldWidth)  << "2  - DynArray(size_t n,const T& val=T())" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    
    	pass = true;
    	DynArray<int> d3(d);
    	if( d3.size()!=0 ) pass=false;
    	if( d3.capacity()!=0 ) pass=false;
    	DynArray<Any> d4(d2);
    	if( d4.size()    !=3 ) pass=false;
    	if( d4.capacity()!=3 ) pass=false;
    	if( d4.pfirst[0] != Any() ) pass=false;
    	if( d4.pfirst[1] != Any() ) pass=false;
    	if( d4.pfirst[2] != Any() ) pass=false;
    	cout<< setw(fieldWidth)  << "3  - DynArray(const DynArray& d)" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    }
    
    void TestDynArray::test_dtor()
    {
    	int cnt = Any::getCount();
    	bool pass = true;
    	{
    		DynArray<Any> d(3);
    	}
    	if( Any::getCount()!=cnt) pass=false;
    	cnt = Any::getCount();
    	DynArray<Any> d(3);
    	d.~DynArray();
    	if( Any::getCount()!=cnt) pass=false;
    	if( d.size()!=0 ) pass=false;
    	if( d.capacity()!=0 ) pass=false;
    	cout<< setw(fieldWidth)  << "4  - ~DynArray()" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    }
    
    void TestDynArray::test_clear()
    {
    	int cnt = Any::getCount();
    	bool pass = true;
    	DynArray<Any> d(4);
    	if( d.size()!=4 )		pass=false;
    	if( d.capacity()!=4 )	pass=false;
    	if( Any::getCount()!=cnt+4) pass=false;
    	d.clear();
    	if( d.size()!=0 )		pass=false;
    	if( d.capacity()!=4 )	pass=false;
    	if( Any::getCount()!=cnt+4) pass=false;
    	cout<< setw(fieldWidth)  << "5  - clear()" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    }
    
    void TestDynArray::test_IndexOperator()
    {
    	int cnt = Any::getCount();
    	bool pass = true;
    	DynArray<int> d(2,3);
    	if( d[0]!=3 )		pass=false;
    	if( d[1]!=3 )		pass=false;
    	d[0]=1;
    	d[1]=2;
    	if( d[0]!=1 )		pass=false;
    	if( d[1]!=2 )		pass=false;
    	cout<< setw(fieldWidth)  << "6  - operator[]( size_t i )" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    
    	pass = true;
    	const DynArray<int> d1(2,4);
    	if( d1[0]!=4 )		pass=false;
    	if( d1[1]!=4 )		pass=false;
    	cout<< setw(fieldWidth)  << "7  - operator[]( size_t i ) const" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    }
    
    void TestDynArray::test_assignment()
    {
    	int cnt = Any::getCount();
    	bool pass = true;
    	cnt = Any::getCount();
    	DynArray<Any> d1(1,Any(2)), d2(2,Any(3));
    	if( Any::getCount()!=cnt+3) pass=false;
    	d2 = d1;
    	if( d2.size()!=1 )			pass=false; // d2 size ok?
    	if( Any::getCount()!=cnt+2) pass=false; // d2 destructor called?
    	if( d2.pfirst[0]!=Any(2) )	pass=false; // elements copied to d2?
    	d1[0] = Any(4);							
    	if( d2.pfirst[0]!=Any(2) )	pass=false; // deep copy implemented
    	const DynArray<Any> d0;	
    	d1 = d0;								// check for const-correctness
    	if( Any::getCount()!=cnt+1) pass=false; 
    	d2 = d2;								
    	if( d2.size()!=1 )			pass=false; // correct handling of self-assignment?
    	if( d2[0]!=Any(2) )			pass=false; // correct handling of self-assignment?
    	cout<< setw(fieldWidth)  << "8  - operator=( const DynArray& d )" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    }
    
    //void TestDynArray::test_push_back()//Fehler beim ausführen?
    //{
    //	int cnt = Any::getCount();
    //	bool pass = true;
    //	DynArray<Any> d;
    //	d.push_back(Any(0));
    //	if( Any::getCount()!=cnt+1) pass=false;
    //	if( d.size()!=1 )			pass=false;
    //	if( d.capacity()!=1 )		pass=false;
    //	if( d.pfirst[0]!=Any(0) )	pass=false;
    //	int cap = d.capacity();
    //	d.push_back(Any(1));
    //	if( Any::getCount()!=cnt+2) pass=false;
    //	if( d.size()!=2 )			pass=false;
    //	if( d.capacity()!=cap*3/2+1 )		pass=false;
    //	if( d.pfirst[0]!=Any(0) )	pass=false;
    //	if( d.pfirst[1]!=Any(1) )	pass=false;
    //	cap = d.capacity();
    //	d.push_back(Any(2));
    //	if( Any::getCount()!=d.capacity())	pass=false;
    //	if( d.size()!=3 )			pass=false;
    //	if( d.capacity()!=cap*3/2+1 )		pass=false;
    //	if( d.pfirst[0]!=Any(0) )	pass=false;
    //	if( d.pfirst[1]!=Any(1) )	pass=false;
    //	if( d.pfirst[2]!=Any(2) )	pass=false;
    //	cout<< setw(fieldWidth)  << "9  - push_back(const T& elem)" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    //}
    
    void TestDynArray::test_reserve()
    {
    	int cnt = Any::getCount();
    	bool pass = true;
    	DynArray<int> d;
    	d.reserve(3);
    	if( d.capacity()<3 )		pass=false;
    	if( d.size()!=0 )			pass=false;
    
    	DynArray<int> d1(3,2);
    	d1[0]=0;
    	d1[1]=1;
    	d1.reserve(4);
    	if( d1.capacity()<4 )		pass=false;
    	if( d1.size()!=3 )			pass=false;
    	if( d1.pfirst[0]!=0 )		pass=false;
    	if( d1.pfirst[1]!=1 )		pass=false;
    	if( d1.pfirst[2]!=2 )		pass=false;
    
    	d1.reserve(2);
    	if( d1.capacity()<3 )		pass=false;
    	if( d1.size()!=3 )			pass=false;
    	if( d1.pfirst[0]!=0 )		pass=false;
    	if( d1.pfirst[1]!=1 )		pass=false;
    	if( d1.pfirst[2]!=2 )		pass=false;
    	cout << setw(fieldWidth)  << "10 - reserve (size_t n)" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    }
    
    void TestDynArray::test_resize()
    {
    	Any::clear();//warum ist count von any 6???  Any::clear(); ist von mir 
                         //provisorisch hinzugefügt wurden, sonnst Failt resize();
    	int cnt = Any::getCount();
    	bool pass = true;
    
    	DynArray<Any> d;
    	d.resize(2);
    	if( Any::getCount()!=d.capacity() )	pass=false;
    	if( d.capacity()<2 )		pass=false;
    	if( d.size()!=2 )			pass=false;
    	if( d.pfirst[0]!=Any() )	pass=false;
    	if( d.pfirst[1]!=Any() )	pass=false;
    
    	d[0] = Any(0);
    	d[1] = Any(1);
    	d.resize(4,Any(3));
    	if( Any::getCount()!=d.capacity() )	pass=false;
    	if( d.capacity()<4 )		pass=false;
    	if( d.size()!=4 )			pass=false;
    	if( d.pfirst[0]!=Any(0) )	pass=false;
    	if( d.pfirst[1]!=Any(1) )	pass=false;
    	if( d.pfirst[2]!=Any(3) )	pass=false;
    	if( d.pfirst[3]!=Any(3) )	pass=false;
    
    	d.resize(3,Any(4));
    	if( Any::getCount()!=d.capacity() )	pass=false;
    	if( d.capacity()<3 )		pass=false;
    	if( d.size()!=3 )			pass=false;
    	if( d.pfirst[0]!=Any(0) )	pass=false;
    	if( d.pfirst[1]!=Any(1) )	pass=false;
    	if( d.pfirst[2]!=Any(3) )	pass=false;
    	cout << setw(fieldWidth)  << "11 - resize(size_t n)" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    }
    

    😕



  • Dieser Thread wurde von Moderator/in Martin Richter aus dem Forum MFC (Visual C++) in das Forum C++ (auch C++0x und C++11) verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.


  • Mod

    T* pfirst, *plast, *pend;
    

    Da alles andere auch kommentiert wird, solltest du auch ein paar Worte zu den Membern verlieren.

    ~DynArray(void){
            T typ;
            for(unsigned int i=0;i<size();i++)
            {
                typ.~T();   
            }
            pfirst = plast = pend = NULL;
            delete [] pfirst;
            delete [] plast;
            delete [] pend;   
        }
    

    was stellst du dir vor, das hier passiert?

    void push_back( const T& val ){
    	size_t sz = size()+1;
    	size_t t = size();
    	T* pdest = new T[sz];
    	std::copy(pfirst,plast,pdest);
    	delete [] pfirst;
    	pfirst = pdest;
    	plast = pend = plast+sz;                //<---geht net :-(
    	std::fill(pfirst+t,plast,val);
    	*if(capacity() == 0){}
    	if(capacity() > 0){}*/
    	}
    

    "geht nicht" ist eine recht unpräzise Beschreibung.

    DynArray& operator=( const DynArray &m ){ 
      if (this != &m)  //oder if (*this != &m) 
      { 
    	/*pfirst = m.pfirst;*/  //<----------warum geht das net 
      } 
      return *this; //Referenz auf das Objekt selbst zurückgeben
    

    ditto

    Sicher gibt es noch andere Probleme, die beim Überfliegen nicht auffallen. Wenn du noch ein main() spendierst, könnten wir das Programm auch selbst testen.


  • Mod

    Was ist deine Frage? Bitte lies den ersten Link in meiner Signatur. Einfach nur "komme nicht weiter" und 400 Zeilen Code hinrotzen ist keine Frage.

    Allgemein scheinst du dynamischen Speicher gründlich missverstanden zu haben, außerdem ist dein Code viel zu kompliziert. Ein paar besondere Hauer, die mir auf den ersten Blick auffallen:

    delete [] pfirst;
            delete [] plast;
            delete [] pend;
    

    plast und pend sind bei dir keine Pointer auf dynamischen Speicher. Folgt in der Regel ein Absturz.

    for(unsigned int i=0;i<size();i++)
            {
                typ.~T();   
            }
    

    Guck mal, was delete wohl macht. Im allgemeinen ist ein expliziter Konstruktoraufruf eine sehr spezielle Sache und für Anfänger ein sicheres Zeichen, dass man gerade Müll programmiert.

    void push_back( const T& val ){
        size_t sz = size()+1;
        size_t t = size();
        T* pdest = new T[sz];
        std::copy(pfirst,plast,pdest);
        delete [] pfirst;
        pfirst = pdest;
        plast = pend = plast+sz;                //<---geht net :-(
        std::fill(pfirst+t,plast,val);
        *if(capacity() == 0){}
        if(capacity() > 0){}*/
        }
    

    Viel zu umständlich. Außerdem ignorierst du deine capacity. Vorgehen sollte sein:
    - Falls noch Platz:
    - Hinten anfügen
    - Fertig
    - Falls kein Platz:
    - Neuen Speicher anfordern (X Mal so viel wie vorher, nicht nur einen mehr)
    - Kopieren
    - Hinten anfügen
    - Alten Speicher freigeben
    - Fertig

    Du solltest auch stärker abstrahieren. Bei dir kommt mindestens 5x das gleiche Schema von new, copy, delete vor. Schreib eine Funktion zur Reallokation, die du stattdessen benutzt.

    Mehr Hinweise gibt's nur gegen eine richtige Frage.



  • Sorry war ein bisschen zu allgemein formuliert.
    Die Frage ist wie implementiere ich den Destruktor,die push_back Funktion und den Operator= in diesem Fall richtig?

    Der Destruktor hat den Test bestanden.

    ~DynArray(void){
    		T typ;
    		for(unsigned int i=0;i<size();i++)//das habe ich gemacht, weil
    		{                                 //im test vom Destruktor ->
    			typ.~T();	
    		}
    		pfirst = plast = pend = NULL;
    		delete [] pfirst;
    
    	}
    
    void TestDynArray::test_dtor()
    {
    	int cnt = Any::getCount();
    	bool pass = true;
    	{
    		DynArray<Any> d(3);
    	}                    //<- an dieser all Objekte gelöscht werden sollen
    	if( Any::getCount()!=cnt) pass=false;
    	cnt = Any::getCount();
    	DynArray<Any> d(3);
    	d.~DynArray();
    	if( Any::getCount()!=cnt) pass=false;
    	if( d.size()!=0 ) pass=false;
    	if( d.capacity()!=0 ) pass=false;
    	cout<< setw(fieldWidth)  << "4  - ~DynArray()" << ( (pass) ? "PASS" : "FAIL" ) << endl;
    }
    

    Die push_back Funktion lässt sich nicht testen. Absturz beim Ausführen.
    Was mache ich hier falsch? danke ""

    void push_back( const T& val ){
    
    		if(capacity() != 0){
    			size_t Pos = plast-pfirst;
    			*(pfirst+Pos) = val;
    			plast= plast+1;
    		}
    
    		if(capacity() == 0){
    			size_t sz = size()*2;
    			size_t t = size();
    			T* pdest = new T[sz];
    			std::copy(pfirst,plast,pdest);
    			*(pdest+t) = val;
    			delete [] pfirst;
    			pfirst = pdest;
    			plast = pend = plast+sz;
    		}
    	}
    

    Und ich weiß bei besten willen nicht wieso das nicht Funktioniert?
    Die Überladung des Operators "=":

    DynArray& operator=( const DynArray &m ){ 
      if (this != &m)  //oder if (*this != rhs) 
      { 
    	pfirst = m.pfirst;  
      } 
      return *this; 
    }
    

  • Mod

    Broly schrieb:

    Der Destruktor hat den Test bestanden.

    Dann ist dein Test falsch oder erkennt den Fehler nicht, weil sich mehrere Fehler gegenseitig wegheben (was dann auch ein Fehler im Test ist, wenn er das nicht erkennen kann).

    Der eigentlich Fehler liegt übrigens im Konstruktor, wo du fröhlich bestehende Objekte mit dem fill überschreibst. Am Ende hast du doppelt so viele Objekte erzeugt wie geplant.

    Die push_back Funktion lässt sich nicht testen. Absturz beim Ausführen.
    Was mache ich hier falsch? danke ""

    Du nutzt keinen Debugger, um die Ursache zu finden.

    void push_back( const T& val ){
    
    		if(capacity() != 0){
    			size_t Pos = plast-pfirst;
    			*(pfirst+Pos) = val;
    			plast= plast+1;
    		}
    
    		if(capacity() == 0){
    			size_t sz = size()*2;
    			size_t t = size();
    			T* pdest = new T[sz];
    			std::copy(pfirst,plast,pdest);
    			*(pdest+t) = val;
    			delete [] pfirst;
    			pfirst = pdest;
    			plast = pend = plast+sz;
    		}
    	}
    

    Ich weiß nicht, wo ich ansetzen soll. So ziemlich jede Zeile hat ein Problem. Ich finde deine Definition von capacity() nicht, aber sie ist schon einmal anders als bei std::vector oder der Code ist falsch. Was soll das Gerechne mit pos? Du hast doch schon einen Zeiger auf das ende, benutz ihn doch! Du erstellst beim Rallokieren wieder elemente und überschreibst sie direkt, gleiches Problem wie oben. Dein Wert für pend ist falsch. Ach, kurz: Alles Mist 😞 . Klingt hart, ist aber so, muss daher gesagt werden dürfen.

    Und ich weiß bei besten willen nicht wieso das nicht Funktioniert?
    Die Überladung des Operators "=":

    DynArray& operator=( const DynArray &m ){ 
      if (this != &m)  //oder if (*this != rhs) 
      { 
    	pfirst = m.pfirst;  
      } 
      return *this; 
    }
    

    Und was ist mit dem Rest deiner Member? Implementier am besten das Copy&Swap-Idiom, da sparst du dir viele mögliche Fehler und kannst auch den Selbstzuweisungstest sein lassen.


  • Mod

    T typ;                               // erstellt ein Objekt des Typs T; falls T eine nicht-POD-Klasse ist, wird auch noch der Defaultkonstruktor aufgerufen
    		for(unsigned int i=0;i<size();i++)
    		{                                 
    			typ.~T();	             // ruft den Destruktor des Objekts typ size-mal auf; falls dieser nicht trivial und size > 1 ist das undefiniert
    		}
    		pfirst = plast = pend = NULL;        // im Prinzip überflüssig; da die Lebenszeit der Member sowieso endet, kommt es nicht darauf an, welchen Inhalt sie zum Schluss haben
    		delete [] pfirst;                    // delete auf Nullzeiger bewirkt nichts; würde delete[] vor dem Nullsetzen aufgerufen wird das array normal zerstört und alle Destruktoren nach bedarf aufgerufen werden
                                                         // hier wird typ nochmal zerstört; wie oben, falls der Destruktur nicht-trivial und size > 0 resultiert UB
    

    Das Ganze sollte also so aussehen:

    ~DynArray(){
            if ( pfirst != 0 )  // optional
                delete [] pfirst;
        }
    

    Die push_back Funktion lässt sich nicht testen. Absturz beim Ausführen.
    Was mache ich hier falsch? danke ""

    void push_back( const T& val ){
    
    		if(capacity() != 0){
    			size_t Pos = plast-pfirst;
    			*(pfirst+Pos) = val;
    			plast= plast+1;
    		}
    
    		if(capacity() == 0){
    			size_t sz = size()*2;
    			size_t t = size();
    			T* pdest = new T[sz];
    			std::copy(pfirst,plast,pdest);
    			*(pdest+t) = val;
    			delete [] pfirst;
    			pfirst = pdest;
    			plast = pend = plast+sz;
    		}
    	}
    

    Die Logik ist fehlerhaft. Eine Reallokation ist genau dann erforderlich, wenn die Kapazität erschöpft ist, also size()==capacity().
    Ansonsten sollte die bereits implementierte Copy-Funktionalität verwendet werden. Mit einer zusätzlichen Funktion swap wird es viel einfacher

    DynArray( const DynArray& d, std::size_t capacity )
            : pfirst(new T[capacity]), plast(pfirst+std::min(d.size(), capacity)), pend(pfirst+capacity)
        {
            std::copy( d.pfirst, d.pfirst+std::min(d.size(), capacity), pfirst );
        }
        void swap(DynArray& other) {
            std::swap( pfirst, other.pfirst );
            std::swap( plast, other.plast );
            std::swap( pend, other.pend );
        }
        void push_back( const T& val ){
            if ( size() == capacity() )
                DynArray( *this, capacity()+1 ).swap( *this );
            *plast++ = val;
        }
    
    DynArray& operator=( const DynArray &m ){ // copy&swap
        DynArray( m ).swap( *this );
        return *this;
    


  • camper schrieb:

    Sicher gibt es noch andere Probleme, die beim Überfliegen nicht auffallen. Wenn du noch ein main() spendierst, könnten wir das Programm auch selbst testen.

    Die main();

    #include <iostream>
    #include "DynArray.h"
    #include "TestDynArray.h"
    
    using namespace std;
    
    int main(){
    
    	TestDynArray test;
    	test.test_all();
    
    	return 0;
    }
    

  • Mod

    camper schrieb:

    ~DynArray(){
            if ( pfirst != 0 )  // optional
                delete [] pfirst;
        }
    

    Optional? Du meinst wohl "vollkommen wirkungslos und verwirrend".


  • Mod

    SeppJ schrieb:

    camper schrieb:

    ~DynArray(){
            if ( pfirst != 0 )  // optional
                delete [] pfirst;
        }
    

    Optional? Du meinst wohl "vollkommen wirkungslos und verwirrend".

    Jetzt bin ich verwirrt :p


  • Mod

    camper schrieb:

    SeppJ schrieb:

    camper schrieb:

    ~DynArray(){
            if ( pfirst != 0 )  // optional
                delete [] pfirst;
        }
    

    Optional? Du meinst wohl "vollkommen wirkungslos und verwirrend".

    Jetzt bin ich verwirrt :p

    Na, wenn pfirst == 0 wäre, dann würde delete[] pfirst; genau gar nix machen (vom Standard garantiert). Dafür verwirrst du nun Broly, der vielleicht denkt, dass diese Abfrage auch nur irgendwie nützlich wäre.



  • Danke erst mal für die Antworten.

    Wenn ich im Destruktor den Speicherplatz freigebe, bevor ich die Membervariablen auf NULL setzte, stürzt das Programm ab. warum, weiß ich nicht.

    ~DynArray(){
            if ( pfirst != 0 )  // optional
                delete [] pfirst;
        }
    

    Der Lösungsansatz für die push_back Funktion ist recht kompliziert zb. habe ich
    solche Implementierungsansätzte

    DynArray( const DynArray& d, std::size_t capacity )
      : pfirst(new T[capacity]), plast(pfirst+std::min(d.size(), capacity)), <- ?
    

    noch nie gesehen 😮 . Ich bin mir sicher, dass diese Lösung richtig ist und sie ihren zweck erfüllt. Aber da ich sie nicht so recht versteh, kann ich sie nicht einfach übernehmen, außerdem besteht sie nicht den Test, in der Test_Unit vom Prof.^^ Meine Frage ist daher zur Funktion push_back, gibt es nicht einen
    Lösungsansatz, wobei man nicht einen weiteren Constructor überladen muss?

    DynArray( const DynArray& d, std::size_t capacity )
            : pfirst(new T[capacity]), plast(pfirst+std::min(d.size(), capacity)), pend(pfirst+capacity)
        {
            std::copy( d.pfirst, d.pfirst+std::min(d.size(), capacity), pfirst );
        }
        void swap(DynArray& other) {
            std::swap( pfirst, other.pfirst );
            std::swap( plast, other.plast );
            std::swap( pend, other.pend );
        }
        void push_back( const T& val ){
            if ( size() == capacity() )
                DynArray( *this, capacity()+1 ).swap( *this );
            *plast++ = val;
        }
    

    Ach und danke, der "=" Operator funktioniert jetzt und die Implementierung war sehr verständlich. 🙂


  • Mod

    SeppJ schrieb:

    camper schrieb:

    SeppJ schrieb:

    camper schrieb:

    ~DynArray(){
            if ( pfirst != 0 )  // optional
                delete [] pfirst;
        }
    

    Optional? Du meinst wohl "vollkommen wirkungslos und verwirrend".

    Jetzt bin ich verwirrt :p

    Na, wenn pfirst == 0 wäre, dann würde delete[] pfirst; genau gar nix machen (vom Standard garantiert). Dafür verwirrst du nun Broly, der vielleicht denkt, dass diese Abfrage auch nur irgendwie nützlich wäre.

    Genau gar nix ist auch recht unpräzise (der Standard an dieser Stelle allerdings auch). Die Diskussion über das Für und Wider einer solchen Abfrage wurde an anderer Stelle bereits geführt, das möchte ich nicht aufwärmen.
    Es gibt im Grunde zwei Argumente, die für eine solche Abfrage sprechen:
    1. Symmetrie: zu jeder Deallokation gehört eine erfolgreiche Allokation (das ist das einzige Argument, das für mich persönlich relevant ist - Programme sollten lesbar bein)
    2. "Genau nix" ist immer noch ein Funktionsaufruf mehr als nötig

    n3337 3.7.4.2 schrieb:

    3 If a deallocation function terminates by throwing an exception, the behavior is undefined. The value of the
    first argument supplied to a deallocation function may be a null pointer value; if so, and if the deallocation
    function is one supplied in the standard library, the call has no effect
    . Otherwise, the behavior is undefined
    if the value supplied to operator delete(void*) in the standard library is not one of the values returned
    by a previous invocation of either operator new(std::size_t) or operator new(std::size_t, const
    std::nothrow_t&) in the standard library, and the behavior is undefined if the value supplied to operator
    delete[](void*) in the standard library is not one of the values returned by a previous invocation of
    either operator new[](std::size_t) or operator new[](std::size_t, const std::nothrow_t&) in the
    standard library.

    Natürlich würde ich jede Ersatzfunktion, die bei einem Nullargument etwas anderes als nix tut als hoffnungslos defekt ansehen. In jedem Fall (mal von unwahrscheinlicher whole-program-Optimierung abgesehen) wird allerdings bei 0 überhaupt ein Funktionsaufruf durchgeführt.

    Ich habe nichts dagegen, die Abfrage wegzulassen, halte diese Wahl aber eben nicht für so eindeutig besser, also schreibe ich optional. Ich glaube auch nicht, das das zu Verwirrung führt.



  • camper schrieb:

    SeppJ schrieb:

    camper schrieb:

    SeppJ schrieb:

    camper schrieb:

    ~DynArray(){
            if ( pfirst != 0 )  // optional
                delete [] pfirst;
        }
    

    Optional? Du meinst wohl "vollkommen wirkungslos und verwirrend".

    Jetzt bin ich verwirrt :p

    Na, wenn pfirst == 0 wäre, dann würde delete[] pfirst; genau gar nix machen (vom Standard garantiert). Dafür verwirrst du nun Broly, der vielleicht denkt, dass diese Abfrage auch nur irgendwie nützlich wäre.

    Genau gar nix ist auch recht unpräzise (der Standard an dieser Stelle allerdings auch). Die Diskussion über das Für und Wider einer solchen Abfrage wurde an anderer Stelle bereits geführt, das möchte ich nicht aufwärmen.
    Es gibt im Grunde zwei Argumente, die für eine solche Abfrage sprechen:
    1. Symmetrie: zu jeder Deallokation gehört eine erfolgreiche Allokation (das ist das einzige Argument, das für mich persönlich relevant ist - Programme sollten lesbar bein)

    Als würde new einen Nullzeiger zurückgeben, wenn es fehlschlägt.. Was meinst du also mit Symmetrie?

    camper schrieb:

    2. "Genau nix" ist immer noch ein Funktionsaufruf mehr als nötig

    Mikrooptimierungen rechtfertigen natürlich jeden Mist.
    Woher weißt du, ob nicht viel mehr Folgendes zutrifft: In den meisten Fällen ist der Zeiger ungleich Null und dann hat man eine Abfrage mehr als nötig. Hunderte solcher Abfragen im Programm blähen den Code auf und verschwenden Platz im Cache und damit Ladezeit. Die Abfrage in delete pro Typ liegt jedoch fast immer schon im Cache.

    camper schrieb:

    Ich habe nichts dagegen, die Abfrage wegzulassen, halte diese Wahl aber eben nicht für so eindeutig besser, also schreibe ich optional. Ich glaube auch nicht, das das zu Verwirrung führt.

    Offensichtlich führt es zu Verwirrung, weil sehr viele die redundante Abfrage machen.


  • Mod

    Broly schrieb:

    Wenn ich im Destruktor den Speicherplatz freigebe, bevor ich die Membervariablen auf NULL setzte, stürzt das Programm ab. warum, weiß ich nicht.

    Dann ist das einer der zahlreichen Fehler, auf die du schon hingewiesen wurdest. Wenn du die Variablen auf 0 setzt und dann freigibst, geschieht wie erwähnt nichts. Es wird aber auch nichts freigegeben. Speicherloch, setzen, Sechs.

    solche Implementierungsansätzte

    DynArray( const DynArray& d, std::size_t capacity )
      : pfirst(new T[capacity]), plast(pfirst+std::min(d.size(), capacity)), <- ?
    

    noch nie gesehen 😮 .

    Das ist bloß eine Initialisierungsliste. Mal googlen. Ist nicht schwierig, aber wichtig.

    Aber da ich sie nicht so recht versteh, kann ich sie nicht einfach übernehmen, außerdem besteht sie nicht den Test, in der Test_Unit vom Prof.^^

    Du hast viele Fehler im Programm. Du wurdest auch schon auf die wichtigsten hingewiesen. Hast du sie bereinigt? Ich wette, Nein. Und falls ich damit Recht habe, sind insbesondere deine anderen Konstruktoren noch falsch.

    Meine Frage ist daher zur Funktion push_back, gibt es nicht einen
    Lösungsansatz, wobei man nicht einen weiteren Constructor überladen muss?

    Klar, mach einfach effektiv das gleiche, bloß alles in push_back. Das was camper gezeigt hat, ist die Logik aus meinem Algorithmus (meine erste Antwort) kombiniert mit dem Hinweis, die ganzen Reallokationen in eine Funktion zu packen (und zwar eine, die auch richtig(!) funktioniert). Das darfst du auch gerne umständlich ohne Zusatzfunktion machen, an das Grundrezept musst du dich aber schon halten.


  • Mod

    TyRoXx schrieb:

    camper schrieb:

    1. Symmetrie: zu jeder Deallokation gehört eine erfolgreiche Allokation (das ist das einzige Argument, das für mich persönlich relevant ist - Programme sollten lesbar bein)

    Als würde new einen Nullzeiger zurückgeben, wenn es fehlschlägt.

    Ich kann nicht folgen. Nochmal genau lesen.

    TyRoXx schrieb:

    camper schrieb:

    2. "Genau nix" ist immer noch ein Funktionsaufruf mehr als nötig

    Mikrooptimierungen rechtfertigen natürlich jeden Mist.
    Woher weißt du, ob nicht viel mehr Folgendes zutrifft: In den meisten Fällen ist der Zeiger ungleich Null und dann hat man eine Abfrage mehr als nötig. Hunderte solcher Abfragen im Programm blähen den Code auf und verschwenden Platz im Cache und damit Ladezeit. Die Abfrage in delete pro Typ liegt jedoch fast immer schon im Cache.

    Allgemeinplätze. Ich weiss es nicht, könnte auch umgekehrt sein. Geht am Thema vorbei.
    Nimmt man deine Argumentation wörtlich, bist du es, der durch Weglassen der Prüfung mikrooptimiert.

    TyRoXx schrieb:

    Offensichtlich führt es zu Verwirrung, weil sehr viele die redundante Abfrage machen.

    Ich kann nicht folgen.

    a oder b ist gleichermaßen möglich.
    viele machen b
    --------------
    es herrscht Verwirrung ??


  • Mod

    camper schrieb:

    a oder b ist gleichermaßen möglich.
    viele machen b
    --------------
    es herrscht Verwirrung ??

    Wie viele von denen, die b machen, können so wie du argumentieren, warum sie es machen? Du dürftest so ziemlich der einzige sein. Die anderen machen es bloß bei diesem Typen mit Ahnung im C++-Forum nach, auch wenn sie nicht verstehen, was es überhaupt soll.

    Aber lass uns das wirklich nicht nochmal aufwärmen. Ich bin schon ganz still und hätte dies hier gar nicht schreiben sollen, aber wo ich es sowieso schon getan habe, kann ich es auch absenden… 🙂


  • Mod

    SeppJ schrieb:

    ... hätte dies hier gar nicht schreiben sollen, aber wo ich es sowieso schon getan habe, kann ich es auch absenden… 🙂

    Für gewöhnlich bin ich auch zu faul, überhaupt darauf einzugehen 🙂

    SeppJ schrieb:

    Wie viele von denen, die b machen, können so wie du argumentieren, warum sie es machen?

    Genau das sollte imo der Sinn dieses Forums sein.
    Wofür sich jemand am Ende ist nicht wichtig, aber die Frage: "Warum?" sollte stets beantwortet werden können.
    Es sollten nicht einfach nur Lösungen vermittelt werden (die haben eine mehr oder minder kurze Halbwertszeit), sondern Grundlagen und Hintergründe.



  • camper schrieb:

    TyRoXx schrieb:

    camper schrieb:

    2. "Genau nix" ist immer noch ein Funktionsaufruf mehr als nötig

    Mikrooptimierungen rechtfertigen natürlich jeden Mist.
    Woher weißt du, ob nicht viel mehr Folgendes zutrifft: In den meisten Fällen ist der Zeiger ungleich Null und dann hat man eine Abfrage mehr als nötig. Hunderte solcher Abfragen im Programm blähen den Code auf und verschwenden Platz im Cache und damit Ladezeit. Die Abfrage in delete pro Typ liegt jedoch fast immer schon im Cache.

    Allgemeinplätze. Ich weiss es nicht, könnte auch umgekehrt sein. Geht am Thema vorbei.
    Nimmt man deine Argumentation wörtlich, bist du es, der durch Weglassen der Prüfung mikrooptimiert.

    Du hast mit den Optimierungen angefangen, die jetzt auf einmal "am Thema vorbeigehen".
    Durch Weglassen der Prüfung wird Redundanz vermieden. Das sollte wichtiger sein als hypothetische Mikrooptimierungen.
    Ich wollte nur zeigen, dass "ein Funktionsaufruf mehr als nötig" kein Argument für die Abfrage ist. Dass es mit Abfrage schneller ist, ist eine unbelegte Vermutung, genau wie meine Argumentation für das Weglassen der Abfrage.
    Ich wage mal zu behaupten, dass das stark von System und Compiler abhängt. Ein schlauer Compiler lässt das if vielleicht auch einfach weg.
    In jedem Fall spielt es keine Rolle, was schneller ist. Der Destruktor und das anschließende Freigeben des Speichers sind um Größenordnungen langsamer als ein Funktionsaufruf.
    Folglich gibt es kein Argument für die Abfrage. Das mit der "Symmetrie" hast du nicht erklärt, es liest sich wie wirres Zeug. Dass delete mit Null klarkommt, hat man wahrscheinlich von free übernommen. Und das verhält sich vermutlich so, weil Aufräum-Code häufig mit Nullzeigern zu tun hat.
    Die eingebaute Abfrage ist ein Feature von C++, eine Abstraktion. Man sollte nicht so tun als gäbe es die nicht.


  • Mod

    Ich bin nicht sicher, was daran schwer verständlich sein sollte, dass Ressourcen nur freigegeben werden können, sofern sie zuvor angefordert wurden.
    Der Versuch, Ressourcen die nicht angefordert wurden, zurückzugeben, ist danach ein Fehler (Nichts als Kategorie).
    Andererseits kann es sinnvoll sein, eine Menge Ressourcen freizugeben, auch wenn diese Menge zufällig leer ist (Nichts als Quantität).
    In Bezug auf Speicher kann in C++ beides ohne Umstände modelliert werden.

    TyRoXx schrieb:

    Die eingebaute Abfrage ist ein Feature von C++, eine Abstraktion. Man sollte nicht so tun als gäbe es die nicht.

    Macht ja auch keiner.

    Oder meinst du eher, dass man dieses Feature nutzen sollte, weil es einmal da ist? Dann interessiert mich, unter welcher Regel du diesen Schluss vom Sein aufs Sollen subsumierst.

    Reichst du beim Lotto deinen Tippschein auch dann ein, wenn du weisst, dass es eine Niete ist?


Anmelden zum Antworten