ошибка "address not from malloc ()" при использовании электрического забора - PullRequest
1 голос
/ 25 июля 2010

Я писал программу тестового примера, чтобы продемонстрировать проблему с моей более крупной программой, и в тестовом примере есть ошибка, которой нет в исходной программе.

Вот файл заголовка:

// 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.Кто-нибудь видит, почему это так?

Ответы [ 3 ]

4 голосов
/ 25 июля 2010
yarl::level::Level l;

Вы создаете автоматическую переменную Level, которая в своем конструкторе создает свой член pathfinder следующим образом:

pathfinder(new path::Pathfinder(this))

Затем в конструкторе Pathfinder он принимает указатель Level, который вы передаете, и присваивает его shared_ptr. shared_ptr затем становится владельцем этого указателя.

Это неверно по нескольким причинам:

  1. A shared_ptr должен использоваться для управления динамически распределяемыми объектами, а не автоматически распределяемыми объектами
  2. Если вы хотите использовать shared_ptr, тогда вы должны использовать его везде: как и сейчас, вы передаете необработанные указатели (например, конструктору Pathfinder, но затем сохраняете их как shared_ptr s. Это просто открывает большую банку владения червями.
  3. Правильный способ присвоения this shared_ptr - это вывод из enable_shared_from_this; заметьте, однако, что вы не можете получить shared_ptr из this в конструкторе.

Когда shared_ptr будет уничтожен, он попытается удалить указатель, которым он управляет. В этом случае, однако, этот указатель относится не к динамически распределяемому объекту (то есть, выделенному с new), а к автоматически распределяемому объекту (т.е. в стеке). Отсюда и ошибка.

Если вам не нужно что-то для владения ресурсом, нет ничего плохого в использовании необработанного указателя (или ссылки, если у вас есть такая опция).

2 голосов
/ 25 июля 2010

Вы создаете shared_ptr из указателя, который не должен управляться shared_ptr. (Указатель this)

Когда последняя копия shared_ptr уничтожена, эта память освобождается - тогда как на самом деле этого не должно быть - this находится в стеке в этом случае.

Существует причина, по которой конструктор shared_ptr является явным - именно для того, чтобы избежать такого незаметного преобразования из обычного указателя, который не должен управляться shared_ptr, в shared_ptr - как только вы передадите такой указатель в shared_ptr , ваша программа обречена - единственный выход - удалить указатель, который вы не намеревались удалить.

Как правило, рекомендуется создавать общий указатель с новым напрямую, например, ptr(new Somethings(x,y,z) - таким образом, вы не рискуете исключить утечку выделенного, но не назначенного в память shared_ptr.

1 голос
/ 25 июля 2010

Level содержит переменную-член LevelMap.Когда Level будет уничтожен, он также уничтожит его LevelMap.

С другой стороны, указатель на этот LevelMap член передается в Pathfinder, что создает shared_ptr<> изпереданный указательЭтот недавно созданный shared_ptr<> думает, что ему принадлежит объект, на который он указывает, и попытается уничтожить его, как только Pathfinder будет уничтожен.

Таким образом, LevelMap уничтожается несколько раз.* В примере LevelMap создается в стеке.Поэтому delete, вызываемый shared_ptr<>, может видеть, что адрес не из кучи, и вы получаете ошибку.Если ваша реальная программа также имеет эту проблему, но все эти объекты расположены динамически, ошибка, вероятно, не будет обнаружена.Вы просто получите тихое повреждение памяти и странные сбои позже.

...