Ошибка "address not from malloc()" при использовании электрического забора

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

Вот заголовочный файл:

// 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 ответа

Решение
yarl::level::Level l;

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

pathfinder(new path::Pathfinder(this))

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

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

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

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

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

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

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

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

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

Level содержит LevelMap переменная-член. Когда Level разрушается, это также разрушит его LevelMap,

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

Итак LevelMap уничтожается несколько раз.

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

Другие вопросы по тегам