Ошибка "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
затем вступает во владение этим указателем.
Это неверно по нескольким причинам:
shared_ptr
должен использоваться для управления динамически распределяемыми объектами, а не автоматически распределяемыми объектами- Если вы хотите использовать
shared_ptr
, тогда вы должны использовать его везде: как и сейчас, вы передаете необработанные указатели (например, конструкторуPathfinder
, но затем сохранить их какshared_ptr
s. Это просто открывает большую червячную банку. - Правильный способ назначить
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<>
можно увидеть, что адрес не из кучи, и вы получаете сообщение об ошибке. Если ваша реальная программа также имеет эту проблему, но все эти объекты расположены динамически, ошибка, вероятно, не будет обнаружена. Вы просто получите тихое повреждение памяти и странные сбои позже.