【问题标题】:Vector of pointers problem指针问题向量
【发布时间】:2010-10-16 02:53:01
【问题描述】:

我在尝试将自定义类的对象 push_back 到以自定义类作为类型的指针向量时遇到了很多麻烦。请参阅下面的代码以及收到的错误。我在 windows xp 上使用带有 CDT 插件和 OpenCV 的 Eclipse。

我花了很多时间试图找到答案,但无济于事! ps 我是学生,指针等不是我的事!

    std:: vector<RoadLine>* LaneChangeDetector::roadLines(IplImage* img_8uc1, IplImage* img_8uc3, IplImage* img_edge, std::vector <RoadLine>* roadVector){

    CvMemStorage* storage = cvCreateMemStorage(0);
    CvSeq* lines = 0;
    CvMemStorage* roadStorage = cvCreateMemStorage(0);
    CvSeq* roadLines = 0;

    // Probabalistic Hough transform returns line segments from edge detected image
    lines = cvHoughLines2( img_edge, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI/180, 50, 200, 200 );

    // Sequence roadlines, lines with correct slope are added to this sequence
    roadLines = cvCreateSeq(0, lines->header_size, lines->elem_size, roadStorage);

    // slope
    double m = 0.0;

    // Point of intersection
    CvPoint poi;

    for(int i = 0; i < lines->total; i++ ){
        CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);
        CvPoint pt1 = line[0];
        CvPoint pt2 = line[1];

        double x1 = double(pt1.x);
        double y1 = double(pt1.y);
        double x2 = double(pt2.x);
        double y2 = double(pt2.y);

        if(pt1.x == pt2.x){
            m = 1.0;
        }
        else{
            m = (double(y2 - y1)/(double(x2 - x1)));
        }

        if( ((m>0.45) && (m<0.75)) || ((m<-0.45) && (m>-0.75)) ){

            // If the slope is between measured parameters add to roadLines sequence for further analysis
            cvSeqPush(roadLines, line);
        }
    }

    // otherRoadLine used for comparison
    CvPoint* otherRoadLine;

    for(int a=0; a<roadLines->total; a++){

        CvPoint* roadLine = (CvPoint*)cvGetSeqElem(roadLines,a);
        CvPoint rl1 = roadLine[0];
        CvPoint rl2 = roadLine[1];
        int lineCount = 0;

        if(a>0){

            // Test the current line against all the previous lines in the sequence.
            // If the current line is far enough away from all other lines then draw it
            for(int b=0; b<a; b++){
                otherRoadLine = (CvPoint*)cvGetSeqElem(roadLines,b);
                if((roadLine->x > ((otherRoadLine->x) + 200)) || (roadLine->x < ((otherRoadLine->x) - 200)) ){
                    lineCount++;
                }
            }
            if(lineCount == a){
                cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
                RoadLine myLine = RoadLine(roadLine, 1);
                roadVector->push_back(myLine); //ERROR OCCURS HERE
                cvShowImage("Plate Detection", img_final);
                cvWaitKey(0);
            }
        }
        else{
            cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
            RoadLine myLine = RoadLine(roadLine, 1);
            roadVector->push_back(myLine //ERROR OCCURS HERE
            cvShowImage("Plate Detection", img_final);
            cvWaitKey(0);
        }
    }

    if(roadVector->size() >= 2){
        int pos = 0;
        RoadLine line1 = roadVector->at(pos);
        RoadLine line2 = roadVector->at(pos + 1);

        CvPoint* A = line1.line;
        CvPoint p1 = A[0];
        CvPoint p2 = A[1];

        int A1 = p1.y - p2.y;
        int B1 = p1.x - p2.x;
        int C1 = (p1.x*p2.y) - (p1.y*p2.x);

        CvPoint* B = line2.line;
        CvPoint p3 = B[0];
        CvPoint p4 = B[1];

        int A2 = p3.y - p4.y;
        int B2 = p3.x - p4.x;
        int C2 = (p3.x*p4.y) - (p3.y*p4.x);

        int det = A2*B1 - A1*B2;

        if(det == 0){
            printf("Lines are parallel");
        }
        else{
            int x = ( C1*(p3.x - p4.x) - (p1.x - p2.x)*C2 )/det;
            int y = ( C1*(p3.y - p4.y) - (p1.y - p2.y)*C2 )/det;

            poi.x = x;
            poi.y = y;

            horizon = poi.x;

            cvCircle(img_final, poi, 10, CV_RGB(255, 0, 0), 2, CV_AA, 0);
        }
    }

    cvShowImage("Plate Detection", img_final);
    cvWaitKey(0);

    return roadVector;
}

自定义类 RoadLine 可以在这里看到

    #include <cv.h>
class RoadLine{
private:
CvPoint* line;
int lane;
public:
RoadLine(CvPoint*, int);
};
RoadLine::RoadLine(CvPoint* aLine, int aLane){
line = aLine;
lane = aLane;
}

从调试中我可以看到“std::vector * roadVector”正在正确初始化。

这是 Eclipse 告诉我的:

3 std::vector<RoadLine, std::allocator<RoadLine> >::push_back() F:\MinGW\include\c++\3.4.5\bits\stl_vector.h:560 0x0043e3f9

4 void std::_Construct<RoadLine, RoadLine>() F:\MinGW\include\c++\3.4.5\bits\stl_construct.h:81 0x0044015d  

并且程序跳转到stl_construct.h中的这一段代码

  template<typename _T1, typename _T2>
inline void
_Construct(_T1* __p, const _T2& __value)
{
  // _GLIBCXX_RESOLVE_LIB_DEFECTS
  // 402. wrong new expression in [some_]allocator::construct
  ::new(static_cast<void*>(__p)) _T1(__value); //DEBUG THROWS ME TO THIS LINE
}

再次感谢任何帮助。

干杯

拍拍

【问题讨论】:

  • 顺便说一句,如果您不太喜欢指针,您可能需要考虑阅读更多关于 C++ 中的引用并使用它们的信息。这是消除一些指针的好方法。例如,实际上没有理由将 road_vector 作为指向向量的指针传递,而不仅仅是作为向量引用。

标签: c++ pointers vector opencv segmentation-fault


【解决方案1】:

您的 RoadLine 类缺少正确的复制器。现在,由于您有一个指向CvPoint 对象的成员,因此您每次push_back 时都会创建指针的副本。这可能是不可取的。

RoadLine::RoadLine(const RoadLine & o){
     line = new CvPoint[ 2 ]; 
     line[ 0 ] = o.line[ 0 ];
     line[ 1 ] = o.line[ 1 ];
     lane = o.lane;
}

RoadLine& operator=(const RoadLine & o){
     if (this != &o) { //Remember to check for self-assignment.
      line = new CvPoint[ 2 ]; 
      line[ 0 ] = o.line[ 0 ];
      line[ 1 ] = o.line[ 1 ];
      lane = o.lane;
     }
     return *this;
}

缩短您的代码:尝试隔离问题:

int main() {
    CvPoint pa[] = { CvPoint(0, 0), CvPoint(100, 100) };
    RoadLine rl1(pa, 1);

    vector<RoadLine> v;
    v.push_back(rl1);

    return 0;
}

这会崩溃吗?

【讨论】:

  • 其实copy-ctor是可以的,你的版本只会更糟,因为line是一个数组,而不是一个CvPoint
  • @jpalecek:那是示例代码。但我明白你的意思。还没仔细看CvPoint *
  • 嗨,所以一旦我复制了 roadVector,我应该在副本上调用 push_back 吗?例如:roadVectorCopy->push_back(myLine); ???
  • @Pat Rohan:push_back 制作了一份副本,并将其存储在向量中。确保你的班级有一个合适的 copy-ctor。语义将取决于您想要什么。
  • @dirkgently:在高层次上,我想返回 roadVector,然后在另一个函数中修改 ite 对象。我知道通过引用传递是做到这一点的方法。我试图展示我的新 RoadLine 类,但它没有格式化。但是无论如何我都会发布它。感谢您迄今为止的帮助
【解决方案2】:

您不使用指针向量,而是使用对象向量。在这种情况下,你的类需要有一个拷贝构造函数,因为 push_back 存储了一个对象的拷贝。

作为一般调试建议,请尝试通过删除尽可能多的代码来解决问题,但仍然会看到不正确的行为。尝试找到最简单的失败示例。

【讨论】:

    【解决方案3】:

    这些类型的错误通常是由不正确的内存管理引起的。遗憾的是,您还没有发布管理记忆的方式。

    如果你可以让它在 linux 系统上运行,你可以尝试在 valgrind 下运行你的程序,这有助于追踪不正确的内存访问/释放。不幸的是,valgrind 在 windows 下不可用,但可能有替代品。

    【讨论】:

      【解决方案4】:

      我已将 RoadLine 的类定义更改为:

      #include <cv.h>
      
      class RoadLine{
      
      private:
          int lane;
      public:
          CvPoint* line;
          RoadLine(CvPoint*, int);
          RoadLine(const RoadLine &);
          ~RoadLine();
          RoadLine& operator=(const RoadLine & o);
      };
      
      RoadLine::RoadLine(CvPoint* aLine, int aLane){
          line = aLine;
          lane = aLane;
      }
      
      RoadLine::RoadLine(const RoadLine & myRoadLine){
          line = new CvPoint[ 2 ]; // CRASHES HERE
          line[ 0 ] = myRoadLine.line[ 0 ];
          line[ 1 ] = myRoadLine.line[ 1 ];
          //line = new CvPoint(*myRoadLine.line);
          lane = myRoadLine.lane;
      }
      
      RoadLine::~RoadLine(){
          delete line;
      }
      
      RoadLine& RoadLine::operator=(const RoadLine & o){
           if (this != &o) { //Remember to check for self-assignment.
            line = new CvPoint[ 2 ];
            line[ 0 ] = o.line[ 0 ];
            line[ 1 ] = o.line[ 1 ];
            lane = o.lane;
           }
           return *this;
      }
      

      这是 RoadLine 类的当前版本

      这就是我实现类的方式:

      else{
              cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
              RoadLine myLine(roadLine, 1);
              roadVector->push_back(myLine); // FROM HERE
              cvShowImage("Plate Detection", img_final);
              cvWaitKey(0);
      }
      

      当 push_back 被调用时,它会调用复制构造函数,但程序在上面突出显示的地方崩溃

      定义了我的向量这一事实有什么不同;

      std::vector<RoadLine>* roadVector
      

      我有一个 CvPoint* 而不是 CvPoint[]

      对不起,如果这些问题看起来很基本

      【讨论】:

        【解决方案5】:

        您的新 RoadLine 课程肯定会导致灾难:

        RoadLine::RoadLine(CvPoint* aLine, int aLane){
            line = aLine;
            lane = aLane;
        }
        
        RoadLine::RoadLine(const RoadLine & myRoadLine){
            line = myRoadLine.line;
            lane = 1;
        }
        
        RoadLine::~RoadLine(){
            delete line;
        }
        

        使用它的代码:

                                if(lineCount == a){
                                        cvLine(img_final, roadLine[0], roadLine[1], CV_RGB(0,0,255), 3, CV_AA, 0 );
                                        RoadLine myLine = RoadLine(roadLine, 1);//create object on the Stack
                                        roadVector->push_back(myLine); //Push COPY of myLine
                                        cvShowImage("Plate Detection", img_final);
                                        cvWaitKey(0);
                                }//Stack-based object "myLine" is automatically destroyed here (leaves scope)
        

        “myLine”的自动销毁将删除“myLine.line”(在RoadLine的dtor中) 但是向量中仍然引用了“myLine.line”(您刚刚推送了它)。

        您必须对行进行深度复制(如其他人建议的那样),如下所示:

        RoadLine::RoadLine(const RoadLine & myRoadLine){
            line = new CvPoint(*myRoadLine.line);//assuming CvPoint can be copy-constructed
            lane = 1;
        }
        

        或者使用 CvLine 对象而不是指针(或其他东西,需要更多上下文)

        编辑: Dirk Gently 的 copy-ctor 有一个错误,因为它会将内存泄漏给之前的“行”成员 应该是:

        RoadLine& operator=(const RoadLine & o){
             if (this != &o) { //Remember to check for self-assignment.
              delete []line;//delete[] vs. delete !
              line = 0;//if next line throws at least we won't double-delete line
              line = new CvPoint[ 2 ]; //this might throw ! should catch (or redesign to get rid of new (prefered)
              line[ 0 ] = o.line[ 0 ];
              line[ 1 ] = o.line[ 1 ];
              lane = o.lane;
             }
             return *this;
        }
        //consistent constructor !
        RoadLine::RoadLine(CvPoint* aLine, int aLane)
            :line(new CvPoint[2]),//might throw, but its better to throw in initializer ! (if you just have one pointer it might be ok to do it like this)
            lane(aLane)
        {
             line[0] = aLine[0];
             line[1] = aLine[1];
        }
        RoadLine::~RoadLine(){
            delete[] line;//also use delete[] vs. normal delete here !
        }
        

        编辑 2:我几乎忘记了我知道它为什么会崩溃!也许您尝试使用 last 和 last+1 CvPoint 构建一对(像这样明显错误的代码)?

        CvPoint Pnts[2] = {CvPoint(0,0),CvPoint(1,1)};
        Roadline Line(&Pnts[1],1);//tries to access Pnts[2] which is one past end !
        

        【讨论】:

        • 嗨,qwerty,感谢您的帮助。关于你的回答,我有一个简短的问题。你是说我只需要改变copy const吗?还是我需要更改代码的实现方式?在复制 cont 行中也是 CvPoint* 类型,所以该行应该是这样的:
        • line = new CvPoint*(*myRoadLine.line);
        • 对不起,我现在看到 line = new CvPoint(*myRoadLine.line); 是正确的。但是我仍然不确定它是如何使用的。再次感谢
        • CvPoint* line = (CvPoint*)cvGetSeqElem(lines,i);我们需要有关此功能的更多信息。我猜它只是返回一个指向更大数组的一部分的指针? (这将改变整个“所有权”场景)
        • 是的,序列本质上是一个向量,无论如何,据我所知。索引 i=0 处的行地址为 0x012f8858,序列行的地址为 0x012f8808。所以它似乎确实返回了一个指向更大序列的指针
        【解决方案6】:

        C++ 的诀窍是把“~”键想象成又大又红,只要你按下它就会响起警铃,即。每当您考虑向类添加析构函数时。

        如果要添加析构函数,则需要复制构造函数和赋值运算符。没有例外。即使您不打算复制该对象,您仍然应该在私有部分中声明它们,这样如果不小心使用它们,编译器就会报错。

        当对象的生命周期受到控制时,您还应该使用引用计数指针而不是原始 C 样式指针(在 C++ 中,这是“RAII”)。如果你这样做,析构函数将从 RoadLine 中消失,而且,神奇的是,你的问题也会消失。

        【讨论】:

          【解决方案7】:

          你没有指针向量。

          std::vector<RoadLine>* roadVector
          

          是一个指向 RoadLine 对象向量的指针。如果你想要一个指针向量,你应该这样做:

          std::vector<RoadLine*> roadVector
          

          这可能对您有所帮助(因为向量将不再调用复制构造函数),但您仍然应该按照其他人的建议将它们整理出来。

          【讨论】:

            猜你喜欢
            • 2011-09-08
            • 2021-09-10
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-06-28
            • 1970-01-01
            • 2011-08-04
            • 1970-01-01
            相关资源
            最近更新 更多