1

我一直在编写一个测试用例程序来演示我的一个更大程序的问题,并且测试用例有一个原始程序没有的错误。

这是头文件:

// compiled with g++ -I/usr/local/bin/boost_1_43_0 -Wall -std=c++0x -g test.cpp

#include <bitset>
#include <boost/shared_ptr.hpp>
#include <vector>

typedef std::vector< std::vector< std::bitset<11> > > FlagsVector;

namespace yarl
{
    namespace path
    {
        class Pathfinder;
    }

    namespace level
    {
        class LevelMap
        {
        // Member Variables
            private:
                int width, height;
                FlagsVector flags;

            public:
                boost::shared_ptr<path::Pathfinder> pathfinder;

        // Member Functions
            LevelMap(const int, const int);

            int getWidth() const {return width;}
            int getHeight() const {return height;}

            bool getFifthBit(const int x, const int y) const
            {
                return flags.at(x).at(y).test(5);
            }
        };



        class Level
        {
        // Member Variables
            public:
                LevelMap map;

        // Member Functions
            public:
                Level(const int w=50, const int h=50);
        };
    }


    namespace path
    {
        class Pathfinder
        {
        // Member Variables
            private:
                boost::shared_ptr<level::LevelMap> clientMap;

        // Member Functions
            public:
                Pathfinder() {}
                Pathfinder(level::LevelMap* cm)
                : clientMap(cm) {}

                void test() const;
        };
    }
}

这是实现文件:

#include <iostream>
#include "test.hpp"
using namespace std;

namespace yarl
{
    namespace level
    {
        LevelMap::LevelMap(const int w, const int h)
        : width(w), height(h), flags(w, vector< bitset<11> >(h, bitset<11>())),
          pathfinder(new path::Pathfinder(this)) 
        {}



        Level::Level(const int w, const int h)
        : map(w,h)
        {
            map.pathfinder->test();
        }
    }



    namespace path
    {
        void Pathfinder::test() const
        {
            int width = clientMap->getWidth();
            int height = clientMap->getHeight();
            cerr << endl;
            cerr << "clientMap->width: " << width << endl; 
            cerr << "clientMap->height: " << height << endl; 
            cerr << endl;
            for(int x=0; x<width; ++x)
            {
                for(int y=0; y<height; ++y)
                {
                    cerr << clientMap->getFifthBit(x,y);
                }
                cerr << "***" << endl; // marker for the end of a line in the output
            }
        }
    }
}

int main()
{
    yarl::level::Level l;
    l.map.pathfinder->test();
}

我将此程序与电子围栏链接,当我运行它时,它会因以下错误而中止:

ElectricFence Aborting: free(bffff434): address not from malloc().

Program received signal SIGILL, Illegal instruction.
0x0012d422 in __kernel_vsyscall ()

来自 gdb 的回溯显示非法指令位于编译器生成的析构函数中Pathfinder,该析构函数在析构其 shared_ptr 时遇到问题。有人知道这是为什么吗?

4

3 回答 3

4
yarl::level::Level l;

您实例化一个自动Level变量,该变量在其构造函数中构造其成员pathfinder,如下所示:

pathfinder(new path::Pathfinder(this))

然后在Pathfinder构造函数中,它获取Level您传入的指针并将其分配给shared_ptr. 然后shared_ptr获取此指针的所有权。

这是不正确的,有几个原因:

  1. Ashared_ptr应该用来管理动态分配的对象,而不是自动分配的对象
  2. 如果你想使用shared_ptr,那么你应该在任何地方使用它:就像现在一样,你传递原始指针(例如,传递给 的构造函数Pathfinder,然后将它们存储为shared_ptrs。这只会打开一大堆所有权蠕虫。
  3. 分配this给 a的正确方法shared_ptr是派生自enable_shared_from_this; 但是请注意,您不能shared_ptrthis构造函数中获取 a 。

shared_ptr被销毁时,它将尝试删除它管理的指针。然而,在这种情况下,该指针不是指向动态分配的对象(即,用 分配new),而是指向自动分配的对象(即,在堆栈上)。因此,错误。

如果您不需要某些东西来获得资源的所有权,那么使用原始指针(或引用,如果您有该选项)没有任何问题。

于 2010-07-25T19:20:08.390 回答
2

您正在shared_ptr从不应由 shared_ptr 管理的指针构造。(this指针)

当最后一个 shared_ptr 的副本被销毁时,这个内存是空闲的——实际上它不应该——this在这种情况下它在堆栈上。

的构造函数是显式的是有原因的shared_ptr——正是为了避免这种从不由 shared_ptr 管理的常规指针到 shared_ptr 的不被注意的转换——一旦你将这样的指针传递给 shared_ptr,你的程序就注定了——唯一的出路是删除您不打算删除的指针。

通常,建议直接使用 new 构造共享指针 - 例如ptr(new Somethings(x,y,z)- 这样您就不会冒异常泄漏已分配但未分配给 shared_ptr 内存的风险。

于 2010-07-25T19:20:23.730 回答
1

Level包含一个LevelMap成员变量。当Level被销毁时,它也会销毁它的LevelMap.

另一方面,指向该LevelMap成员的指针被传递给,它从传递的指针Pathfinder创建一个。shared_ptr<>这个新创建shared_ptr<>的对象认为它拥有它指向的对象,并且一旦Pathfinder被销毁就会尝试销毁它。

所以LevelMap被破坏了好几次。

在示例中,LevelMap是在堆栈上创建的。因此,delete被调用者shared_ptr<>可以看到地址不是来自堆,并且您会收到错误。如果你的真实程序也有这个问题,但所有这些对象都是动态分配的,那么可能不会检测到错误。以后你只会得到无声的内存损坏和奇怪的崩溃。

于 2010-07-25T19:26:05.557 回答