У меня есть код, в котором я создаю пользовательские данные (круг), которые передаются в Lua, которые я также сохраняю как указатель на C++ и помещаю в вектор. После вызова lua_close(L) я пытаюсь удалить указатель, но это приводит к сбою программы. Я тоже пробовал бесплатно, но результат тот же
Затем из этого поста я узнал, что Lua автоматически освобождает пользовательские данные, и поэтому мне не нужно удалять указатель. Это правда?
Я настроен скептически, потому что после того, как я закрываю Lua с помощью lua_close(), я все еще могу получить доступ к кругу, перемещать его, рисовать и т. д.
Если я не удалю указатель на пользовательские данные, это приведет к утечке памяти?
Я пробовал удалить и освободить указатель, делая это перед закрытием Lua, но все просто вылетало.
Вот как я создаю пользовательские данные
int wrap_createCircle(lua_State* luaState){
float radius = lua_tonumber(luaState, 1);
CircleShape* circle = (CircleShape*)lua_newuserdata(luaState, sizeof(CircleShape));
new (circle) CircleShape(radius);
luaL_getmetatable(luaState, "CircleMetaTable");
lua_setmetatable(luaState, -2);
return 1;
}
Согласен с @Jarod42. Я вижу как минимум три способа использования Lua userdata. 1. Указатель на круг хранится в легких пользовательских данных. 2. Объект круга хранится в полных пользовательских данных. 3. Указатель на круг хранится в полных пользовательских данных. Я думаю, что вариант 3 является лучшим для данного варианта использования.





Когда вы звоните lua_newuserdata, вы создаете полные пользовательские данные. Указатель, возвращаемый этой функцией, будет автоматически освобожден Lua, когда соответствующий объект пользовательских данных исчезнет, поэтому утечки памяти не произойдет, и вам не следует пытаться освободить его самостоятельно.
Я настроен скептически, потому что после того, как я закрываю Lua с помощью lua_close(), я все еще могу получить доступ к кругу, перемещать его, рисовать и т. д.
Использование после освобождения в C++ — это неопределенное поведение, что означает, что может произойти буквально все, что угодно, например, успешный доступ, как если бы память все еще была действительна.
Еще одно замечание о вашей функции: стандарт C++ говорит об этом в разделе 6.7.3 [basic.life]:
Для объекта типа класса программе не требуется явно вызывать деструктор перед повторным использованием или освобождением памяти, которую занимает объект; однако, если нет явного вызова деструктора или если выражение удаления (7.6.2.8) не используется для освобождения памяти, деструктор не вызывается неявно, и любая программа, которая зависит от побочных эффектов, создаваемых деструктором, имеет неопределенное поведение.
Поэтому вам, вероятно, следует добавить метаметод __gc, который вызывает circle->~CircleShape();.
Я думаю, что вам нужно что-то вроде этого: когда GC запускается, он вызывает метод deleteCircleShape, который удалит CircleShape *, таким образом, будет вызван деструктор CircleShapes, а затем lua удалит указатель указателя после возврата GC.
P.S. если у вас также есть этот указатель, хранящийся в векторе, вы можете добавить код в деструктор (или метод deleteCircleShape), чтобы удалить его из вектора, чтобы вы не получали никаких нулевых ошибок, если вы зацикливаете указанный вектор для рисования и т.д.
#include <iostream>
#include "lua.hpp"
#define LUA_FUNC __declspec(dllexport)
class CircleShape {
float radius_;
public:
explicit CircleShape(const float radius) : radius_(radius) {
std::cout << "CircleShape Constructor Called" << std::endl;
}
~CircleShape() {
std::cout << "CircleShape Destructor Called" << std::endl;
}
[[nodiscard]] float getRadius() const { return radius_; }
};
LUA_FUNC int NewCircleShape(lua_State* L) {
const auto radius = static_cast<float>(luaL_checknumber(L, 1));
auto** p_circle_shape = static_cast<CircleShape**>(lua_newuserdata(L, sizeof(CircleShape*)));
*p_circle_shape = new CircleShape(radius);
luaL_getmetatable(L, "CircleMetaTable");
lua_setmetatable(L, -2);
return 1;
}
LUA_FUNC int DeleteCircleShape(lua_State* L) {
auto** p_circle_shape = static_cast<CircleShape**>(luaL_checkudata(L, 1, "CircleMetaTable"));
luaL_argcheck(L, *p_circle_shape != NULL, 1, "Error blah");
// if pointer is stored somewhere like a vector remove it now!!!
delete* p_circle_shape;
return 0;
}
LUA_FUNC int GetCircleShapeRadius(lua_State* L) {
auto** p_circle_shape = static_cast<CircleShape**>(luaL_checkudata(L, 1, "CircleMetaTable"));
const float radius = (*p_circle_shape)->getRadius();
lua_pushnumber(L, radius);
return 1;
}
extern "C" LUA_FUNC int luaopen_CircleShapeModule(lua_State * L) {
static constexpr luaL_Reg circle_shape_methods[] = {
{ "getRadius", &GetCircleShapeRadius },
{nullptr, nullptr}
};
static constexpr luaL_Reg circle_shape[] = {
{ "new", &NewCircleShape },
{nullptr, nullptr}
};
luaL_newlib(L, circle_shape);
luaL_newmetatable(L, "CircleMetaTable");
luaL_newlib(L, circle_shape_methods);
lua_setfield(L, -2, "__index");
lua_pushstring(L, "__gc");
lua_pushcfunction(L, DeleteCircleShape);
lua_settable(L, -3);
lua_pop(L, 1);
return 1;
}
Показать код. Есть несколько способов передать пользовательские данные в Lua...