2008-11-19 16 views
9

Ich habe eine abstrakte Basisklasse namens Shape, aus der beide Kreis und Rechteck abgeleitet sind, aber wenn ich den folgenden Code in VS 2005 ausführen, bekomme ich den Fehler Debug Assertion fehlgeschlagen. Zur gleichen Zeit habe ich nicht == Operator in irgendeiner KlasseVektor Iterator nicht dereferencable

Ausdruck: Vector Iterator nicht dereferencable, was ist der Grund dafür.

vector<Shape*> s1; 
    s1.push_back(new Circle(point(1,2),3)); 
    s1.push_back(new Circle(point(4,3),5)); 
    s1.push_back(new Rectangle(point(1,1),4,5)); 

    vector<Shape*> s2(s1); 
    reverse(s1.begin(),s1.end()); 

    (*find(s1.begin(),s1.end(),new Circle(point(1,2),3)))->move(point(10,20)); 

Antwort

12

Ganz einfach:

  • Fund schlägt fehl, da die neu erstellen Kreis kann nicht mit dem Vergleich Form *
  • ein gescheiterter Fund gibt das Ende Iterator im Vektor gefunden werden, die als gefangen nicht deferencable ist durch eine Debug-Assertion

Denn es wie Sie arbeiten möchten, müssen Sie Form vergleichen, nicht-Form *

Wie in anderen Antworten erwähnt, boost::ptr_vector ist eine einfache Möglichkeit, dies zu erreichen.

+0

Und um Shape-Objekte anstelle von Shape Pointer zu vergleichen, verwenden Sie boost :: ptr_vector. Dies ermöglicht Ihnen, die normalen Algorithmen viel natürlicher zu verwenden. –

6

Wie @David Pierre schlägt vor: find ist value-based: Es sieht im Bereich der Iteratoren für einen Zeiger (z. B. 0x0F234420), der den Zeiger auf die gerade erstellte new Circle(point(1,2),3) entspricht. Da das ein neues Objekt ist, wird es nicht da sein.

Sie können dies umgehen, indem Sie find_if mit einem Operator verwenden, der die Objekte vergleicht, auf die der Zeiger verweist.

Das Kriterium sollte jedoch in der Lage sein, zwischen Formtypen zu unterscheiden.

class Shape { 
public: 
    //amongst other functions 
    virtual bool equal(const Shape*) const = 0; 
}; 

class Circle : public Shape { 
public: 
    bool equal(const Shape* pOther) const { 
     const Circle* pOtherCircle = dynamic_cast<const Circle*>(pOther); 
     if(pOtherCircle == NULL) return false; 
     // compare circle members 
    } 
}; 

class Rectangle : public Shape { 
public: 
    bool equal(const Shape* pOther) const { 
     const Rectangle* pOtherR = dynamic_cast<const Rectangle*>(pOther); 
     if(pOtherR == NULL) return false; 
     // compare rectangle members 
    } 
}; 



Shape* pFindThis = new Circle(point(1,2),3); 
vector<Shape*>::const_iterator itFound = find_if(s1.begin(),s1.end(), 
    bind1st(mem_fun(&Shape::equal), pFindThis))); 
delete pFindThis; //leak resolved by Mark Ransom - tx! 

if(itFound != s1.end()) { 
    (*itFound)->move(point(10,20)); 
} 
+0

Löscht Bind1st den zweiten Parameter, wenn es fertig ist? Wenn nicht, haben Sie ein Speicherleck. Ich denke du willst nur ein lokales Temporäres dort haben. –

+0

Sie haben absolut recht. – xtofl

2

Dies ist ein guter Grund, boost :: ptr_vector zu verwenden.

Es behandelt nicht nur die Tatsache, dass Ihre Objekte zerstört werden müssen.
xtofl @: Sie haben den virtuellen Destruktor vergessen.

Aber es macht auch die Mitglieder aussehen wie Objekte durch die Rückgabe von Referenzen und nicht Zeiger. Dies ermöglicht es Ihnen, die Standardalgorithmen viel natürlicher zu verwenden, als mit Zeigern in Ihrer "gleichen" Funktion herumzuspielen (was sehr un C++ -ähnlich ist).

#include <boost/ptr_container/ptr_vector.hpp> 
#include <iostream> 

class Shape 
{ 
    public: 
     ~Shape() {} 
     bool operator==(Shape const& rhs) const 
     { 
      if (typeid(*this) != typeid(rhs)) 
      { 
       return false; 
      } 

      return this->isEqual(rhs); 
     } 
    private: 
     virtual bool isEqual(Shape const& rhs) const = 0; 
}; 

class Circle: public Shape 
{ 
    public: 
     Circle(int r) 
      :radius(r) 
     {} 
    private: 
     virtual bool isEqual(Shape const& r) const 
     { 
      Circle const& rhs = dynamic_cast<Circle const&>(r); 
      return radius == rhs.radius; 
     } 
     int radius; 
}; 
class Rectangle: public Shape 
{ 
    public: 
     Rectangle(int h,int w) 
      :height(h) 
      ,width(w) 
     {} 
    private: 
     virtual bool isEqual(Shape const& r) const 
     { 
      Rectangle const& rhs = dynamic_cast<Rectangle const&>(r); 
      return (height == rhs.height) && (width == rhs.width); 
     } 
     int height; 
     int width; 
}; 


int main() 
{ 

    boost::ptr_vector<Shape> data; 

    data.push_back(new Circle(5)); 
    data.push_back(new Circle(6)); 
    data.push_back(new Rectangle(7,4)); 

    boost::ptr_vector<Shape>::iterator f; 
    f = find(data.begin(),data.end(),Circle(6)); 

    std::cout << "Find(" << (f - data.begin()) << ")" << std::endl; 


} 
+0

Es scheint ein wenig gefährlich zu sein, sich auf operator == zu verlassen, um zu bestätigen, dass die Formen vom selben Typ sind, bevor isEqual aufgerufen wird. Ich würde es lieber sehen, dass check in isEqual selbst ist, wie es xtofl getan hat. –

+0

Ich bevorzuge es in 'operator ==' so wird der Code nicht wiederholt. Warum willst du es in isEqual? Hinweis Shape ist eine rein virtuelle Instanz, es kann keine Instanzen geben. Ich verstehe nicht, warum du denkst, es ist dangrorus? –

+0

Ich habe einen Zweifel hinsichtlich der Überprüfung von Typiden in Basisklassen. Warum wird das eigentlich benötigt? Weil call to correct isEqual function zur Laufzeit wegen Polymorphism aufgelöst wird. – Poorna

Verwandte Themen