C++和Lua交互总结

C++和Lua交互总结

  • Chapter1. C++和Lua交互总结
    • 一、Lua与C++的交互机制——Lua堆栈
    • 二、堆栈的操作
    • 三、C++ 调用 Lua
      • 1)C++获取Lua值
      • 2)C++调用Lua函数
      • 示例:
    • 四、Lua 调用 C++
      • 包装C++函数
    • 最后总结一下


Chapter1. C++和Lua交互总结

原文链接:https://blog.csdn.net/qq826364410/article/details/88624824/

一、Lua与C++的交互机制——Lua堆栈

Lua和C++ 的交互机制的基础在于Lua提供了一个虚拟栈,C++ 和Lua之间的所有类型的数据交换都通过这个栈完成。无论何时C++想从Lua中调用一个值,被请求的值将会被压入栈,无论何时C++想要传递一个值给Lua,首先将整个值压栈,然后就可以在Lua中调用。

Lua中,对虚拟栈提供正向索引和反向索引两种索引方式,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。重要!后面所有的交互,都是基于Lua的虚拟栈来通信。假设当前Lua的栈中有5个元素,如下图所示:
在这里插入图片描述

二、堆栈的操作

因为lua与c/c++是通过栈来通信,lua提供了C API对栈进行操作。

我们先来看一个最简单的例子:

#include <iostream>  
#include <string.h>  
using namespace std;extern "C"
{
#include "lua.h"  
#include "lauxlib.h"  
#include "lualib.h"  
}int main()
{//1.创建一个state  // luaL_newstate返回一个指向堆栈的指针lua_State *L = luaL_newstate();//2.入栈操作  lua_pushstring(L, "I am so cool~");lua_pushnumber(L, 20);//3.取值操作  if (lua_isstring(L, 1)) {             //判断是否可以转为string  cout << lua_tostring(L, 1) << endl;  //转为string并返回  }if (lua_isnumber(L, 2)) {cout << lua_tonumber(L, 2) << endl;}//4.关闭state  lua_close(L);getchar();return 0;
}

在执行这个例子之前,我们需要引入Lua.lib静态库,也就是上文中extern "C"中执行的include。

extern “C”
主要作用就是为了能够正确实现C++ 代码调用其他C语言代码。加上extern “C”后,会指示编译器这部分代码按c语言的进行编译,而不是C++ 的。由于C++ 支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名,比如_Log_int_int;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名,比如_Log。

void Log(int a, int b){}

Lua源代码的下载地址: http://www.lua.org/ftp/,但没有提供相应的静态库,只有.c和.h文件,需要我们自己生成静态库。

生成Lua静态库方法: https://blog.csdn.net/qq826364410/article/details/88563408

也可以直接下载我生成好的静态库:https://download.csdn.net/download/qq826364410/11029611

好了,我们有了Lua静态库,就可以开心的玩耍了。

引入Lua静态库

  • 首先,新建一个Visual C++的空项目,右键点击工程属性,选择VC++目录,
  • 把lua工程中的.h头文件所在的目录加到包含目录中
  • 把Lua静态库文件所在的目录加到库目录中,
  • 最后,点击链接器->输入->附加依赖项->加上生成的Lua静态库,比如Lua.lib,记得用分号";"与其他库隔开。
    OK,大功告成!
    Lua虚拟栈在源码中是如何实现的:

Lua栈是在创建lua_State时建立的,TValue stack[max_stack_len] ,欲知内情可以查 Lua源码lstate.c的stack_init函数
Lua栈可以存储数字,字符串,表,闭包等,它们最终都用TValue这种数据结构来保存 。
在这里插入图片描述
TValue结构对应于lua中的所有数据类型, 是一个{值, 类型} 结构, 这就lua中动态类型的实现, 它把值和类型绑在一起, 用tt记录value的类型, value是一个联合结构, 由Value定义, 可以看到这个联合有四个域, 先说明简单的
p – 可以存一个指针, 实际上是lua中的light userdata结构
n – 所有的数值存在这里, 不管是int , 还是float
b – Boolean值存在这里, 注意, lua_pushinteger不是存在这里, 而是存在n中, b只存布尔
gc – 其他诸如table, thread, closure, string需要内存管理垃圾回收的类型都存在这里
gc是一个指针, 它可以指向的类型由联合体GCObject定义, 从图中可以看出, 有string, userdata, closure, table, proto, upvalue, thread
可以的得出如下结论:
1. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关。
2. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收。

三、C++ 调用 Lua

C++ 可以获取Lua中的值,可以调用Lua函数,还可以修改Lua文件

1)C++获取Lua值

使用lua_getglocal来获取值,然后将其压栈
使用lua_toXXX将栈中元素取出转成相应的C++类型的值
如果Lua值为table类型的话,通过lua_getfield和lua_setfield获取和修改表中元素的值

2)C++调用Lua函数

使用lua_getglobal来获取函数,然后将其压入栈;
如果这个函数有参数的话,就需要依次将函数的参数也压入栈;
这些准备工作都准备就绪以后,就调用lua_pcall开始调用函数了,调用完成以后,会将返回值压入栈中;

示例:

新建一个简单的lua放到工程的同级目录下
hello.lua

str = "I am x-man."
tbl = {name = "DC", id = 20114442}
function add(a,b)return a + b
end

然后,我们写一个Lua1.cpp来访问lua中的数据

#include <iostream>
#include <string.h>
using namespace std;extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
int main()
{//1.创建Lua状态,返回一个指向堆栈的指针lua_State *L = luaL_newstate();if (L == NULL){return;}//2.加载lua文件int bRet = luaL_loadfile(L, "hello.lua");if (bRet){cout << "load file error" << endl;return;}//3.运行lua文件bRet = lua_pcall(L, 0, 0, 0);if (bRet){cout << "pcall error" << endl;return;}//4.读取全局变量,// 1.把 str 压栈 2.由lua去寻找全局变量str的值,并将str的值返回栈顶(替换str)// 如果存在相同命名的其他变量、table或函数,就会报错(读取位置发生访问冲突)lua_getglobal(L, "str");// -1取出栈顶元素,转化为stringstring str = lua_tostring(L, -1);cout << "str = " << str.c_str() << endl;//5.读取table,把table压栈lua_getglobal(L, "tbl");//-------------------------------// 1.把name压入栈中,2.由lua去寻找table中name键的值,并将键值返回栈顶(替换name)// 相当于lua_pushstring(L, "name") + lua_gettable(L, -2)执行结果是一样的lua_getfield(L, -1, "name");// 把name压入栈中//lua_pushstring(L, "name");// 弹出栈上的name,并从表中找到name的键值,把结果放在栈上相同的位置//lua_gettable(L, -2);//---------------------------------str = lua_tostring(L, -1);// 因为table在栈顶的下面,所以取-2,把id压栈,由lua找到table中id键的值,并返回栈顶(替换id)lua_getfield(L, -2, "id");// id的值已经在栈顶,取-1int id = lua_tonumber(L, -1);cout << "tbl:name = " << str.c_str() << endl;cout << "tbl:id = " << id << endl;// 读取函数,// 1.将函数add放入栈中,2.由lua去寻找函数add,并将函数add返回栈顶(替换add)。lua_getglobal(L, "add");		// 获取函数,压入栈中lua_pushnumber(L, 10);			// 压入第一个参数lua_pushnumber(L, 20);			// 压入第二个参数// 栈过程:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。int iRet = lua_pcall(L, 2, 1, 0);if (iRet)						{// 调用出错const char *pErrorMsg = lua_tostring(L, -1);cout << pErrorMsg << endl;lua_close(L);return;}if (lua_isnumber(L, -1))        //取值输出{int fValue = lua_tonumber(L, -1);cout << "Result is " << fValue << endl;}// 栈的索引方式可以是正数也可以是负数,区别是:1永远表示栈底元素,-1永远表示栈顶元素。//至此,栈中的情况是://=================== 栈顶 =================== // 索引    类型      值// 5或-1   int       30 // 4或-2   int       20114442// 3或-3   string    shun // 2或-4   table	 tbl// 1或-5   string	 I am so cool~//=================== 栈底 =================== lua_pushstring(L, "Master");// 会将"Master"值出栈,保存值,找到到table的name键,如果键存在,存储到name键中lua_setfield(L, 2, "name");// 读取lua_getfield(L, 2, "name");str = lua_tostring(L, -1);cout << "tbl:name = " << str.c_str() << endl;// 创建新的tablelua_newtable(L);lua_pushstring(L, "A New Girlfriend");lua_setfield(L, -2, "name");// 读取lua_getfield(L, -1, "name");str = lua_tostring(L, -1);cout << "newtbl:name = " << str.c_str() << endl;//7.关闭state// 销毁指定 Lua 状态机中的所有对象, 并且释放状态机中使用的所有动态内存。// (如果有垃圾收集相关的元方法的话,会调用它们)lua_close(L);getchar();return 0;
}

代码中,已经有很详细的注释了,这里总结一下:

1. 读取lua的全局变量:

lua_getglobal(L, "str"); 

内部实现:1.把全局变量 str 里的值压栈 2.由lua去寻找全局变量str的值,并将str的值返回栈顶(替换str)
注意:如果存在相同命名的其他变量、table或函数,就会报错(读取位置发生访问冲突)

2. 读取table中的键值:

lua_getglobal(L, "tbl");
lua_getfield(L, -1, "name");  

lua_getglobal方法跟上面的实现是一样的。

lua_getfield方法:

内部实现:1.把name压入栈中,2.由lua去寻找table中name键的值,如果键存在,将键值返回栈顶(替换name)

注意:这里的参数-1,就是表示把table中的键值返回到栈顶。

  1. 调用lua中的函数:
lua_getglobal(L, "add");		// 获取函数,压入栈中
lua_pushnumber(L, 10);			// 压入第一个参数
lua_pushnumber(L, 20);			// 压入第二个参数
// 栈过程:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈
// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数,
// iRet为0表示调用成功
int iRet = lua_pcall(L, 2, 1, 0);

四、Lua 调用 C++

Lua可以调用由C++定义、实现具体的函数
步骤:

  • 将C++的函数包装成Lua环境认可的Lua_CFunction格式
  • 将包装好的函数注册到Lua环境中
  • 像使用普通Lua函数那样使用注册函数

包装C++函数

为了从Lua脚本中调用C++函数,需要将被调用的C++函数从普通的C++函数包装成Lua_CFunction格式,并需要在函数中将返回值压入栈中,并返回返回值个数。

int (*lua_CFunction) (lua_State *L);

例如有一个C++函数:

int add(int a,int b)
{return a+b;
}

包装为:

int add(lua_state *L)
{int a = lua_tonumber(-1);int b = lua_tonumber(-2);int sum = a+b;// 将返回值压入栈中lua_pushnumber(L,sum);// 返回返回值个数return 1;
}

示例:
新建一个简单的lua放到工程的同级目录下
avg.lua

avg, sum = average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)print("age", age)
for k,v in pairs(newTable) do print("k = ",k," v = ",v)
end
print("name", newTable.name)

然后,创建一个Lua2.cpp:

#include <stdio.h>extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}static int average(lua_State *L)
{/* 得到参数个数 */int n = lua_gettop(L);double sum = 0;int i;/* 循环求参数之和 */for (i = 1; i <= n; i++){/* 求和 */sum += lua_tonumber(L, i);}/* 压入平均值 */lua_pushnumber(L, sum / n);/* 压入和 */lua_pushnumber(L, sum);/* 返回返回值的个数 */return 2;
}int main(int argc, char *argv[])
{/* 初始化Lua *//* 指向Lua解释器的指针 */lua_State* L = luaL_newstate();/* 载入Lua基本库 */luaL_openlibs(L);/* 注册函数 */lua_register(L, "average", average);// 设置lua中的全局变量lua_pushinteger(L, 18);   //入栈lua_setglobal(L, "age");  //1.先将18值出栈,保存值,2.在lua中,把值存储到全局变量age中// 设置lua中tablelua_newtable(L); //创建一张空表,并将其压栈lua_pushstring(L, "lili");// 入栈// 1.先将"lili"值出栈,保存值,2.找table的name键,如果键存在,存储到name键中lua_setfield(L, -2, "name");//栈顶是lili,新创建的table在lili下,所以是-2// 将table赋值到lua中,并弹出tablelua_setglobal(L, "newTable");/* 运行脚本 */luaL_dofile(L, "avg.lua");/* 清除Lua */lua_close(L);/* 暂停 */printf("Press enter to exit…");getchar();return 0;
}

1. 读取C++的变量:

// 设置lua中的全局变量
lua_pushinteger(L, 18);   //入栈
lua_setglobal(L, "age");  

lua_setglobal(L, “age”) 内部实现: 1.先将值出栈,保存值,2.在lua中,把值存储到全局变量age中

2. 调用在C++中创建的新表的元素:

// 设置lua中table
lua_newtable(L); //创建一张空表,并将其压栈
lua_pushstring(L, "lili");// 入栈
// 1.先将"lili"值出栈,保存值,2.找table的name键,如果键存在,存储到name键中
lua_setfield(L, -2, "name");//栈顶是lili,新创建的table在lili下,所以是-2
// 将table赋值到lua中,并弹出table
lua_setglobal(L, "newTable");

lua_setglobal(L, “newTable”) 内部实现: 1.先将table出栈,保存table,2.在lua中,存储到newTable表中

在lua中,print(“name”, newTable.name),使用newTable.name调用在C++中创建的新表的元素。

3. 调用C++中的函数:

将C++的函数包装成Lua环境认可的Lua_CFunction格式
将包装好的函数注册到Lua环境中
像使用普通Lua函数那样使用注册函数
包装C++函数:

static int average(lua_State *L)
{/* 得到参数个数 */int n = lua_gettop(L);double sum = 0;int i;/* 循环求参数之和 */for (i = 1; i <= n; i++){/* 求和 */sum += lua_tonumber(L, i);}/* 压入平均值 */lua_pushnumber(L, sum / n);/* 压入和 */lua_pushnumber(L, sum);/* 返回返回值的个数 */return 2;
}

将包装好的函数注册到Lua环境中

/* 注册函数 */
lua_register(L, "average", average);

在lua中正常调用

avg, sum = average(10, 20, 30, 40, 50)

4.把C++的函数封装成模块
把C++的函数封装成模块:

①将C++的函数包装成Lua环境认可的Lua_CFunction格式,调用luaL_newlib,放入到一个lua表中压入栈里。

②将自定义模块,注册到Lua环境中。

③在lua中,加上自定义模块名调用C++函数。

avg.lua,这里的lua文件,在调用C++的函数时,需要加上自定义的模块名。 比如,我们定义模块名为mylib。

avg, sum = mylib.average(10, 20, 30, 40, 50)
print("The average is ", avg)
print("The sum is ", sum)

Lua1.cpp

#include <stdio.h>extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}static int average(lua_State *L)
{/* 得到参数个数 */int n = lua_gettop(L);double sum = 0;int i;/* 循环求参数之和 */for (i = 1; i <= n; i++){/* 求和 */sum += lua_tonumber(L, i);}/* 压入平均值 */lua_pushnumber(L, sum / n);/* 压入和 */lua_pushnumber(L, sum);/* 返回返回值的个数 */return 2;
}// 1. 列出需要封装的C++函数
// luaL_Reg为注册函数的数组类型
static const luaL_Reg mylibs_funcs[] = {{ "average", average },{ NULL, NULL }
};// 2. 将所有函数放到一个table中,并压入栈中
int lua_openmylib(lua_State* L) {//创建一个新的表,将所有函数放到一个table中//将这个table压到stack里luaL_newlib(L, mylibs_funcs);return 1;
}// 3. 将自定义模块加到注册列表里
static const luaL_Reg lua_reg_libs[] = {{ "base", luaopen_base },{ "mylib", lua_openmylib }, //这里为自定义的模块名字mylib{ NULL, NULL }
};int main(int argc, char *argv[])
{/* 初始化Lua *//* 指向Lua解释器的指针 */lua_State* L = luaL_newstate();/* 载入Lua基本库 */luaL_openlibs(L);//4. 注册让lua使用的模块const luaL_Reg* lua_reg = lua_reg_libs;for (; lua_reg->func; ++lua_reg) {// 加载模块// 首先查找 package.loaded 表, 检测 modname 是否被加载过。 // 如果被加载过,require 返回 package.loaded[modname] 中保存的值。// 如果 modname 不在 package.loaded 中, 则调用函数 openf ,并传入字符串 modname。// 将其返回值置入 package.loaded[modname]。// 如果最后一个参数为真, 同时也将模块设到全局变量 modname 里。在栈上留下该模块的副本。luaL_requiref(L, lua_reg->name, lua_reg->func, 1);// 从栈中弹出 1 个元素lua_pop(L, 1);}/* 运行脚本 */luaL_dofile(L, "avg.lua");/* 清除Lua */lua_close(L);/* 暂停 */printf("Press enter to exit…");getchar();return 0;
}

5. Lua调用C++类注册生成的Lua模块
由于篇幅的限制,请移步:https://blog.csdn.net/qq826364410/article/details/88652441

6. 在Lua中以面向对象的方式使用C++注册的类
由于篇幅的限制,请移步:https://blog.csdn.net/qq826364410/article/details/88639408

Lua和C++交互:全局数组交互
https://blog.csdn.net/qq826364410/article/details/88713839

补充
这里补充其他一些栈操作:

int   lua_gettop (lua_State *L);	        //返回栈顶索引(即栈长度)
// lua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数量。
// 如果值比原栈顶高,则高的部分nil补足,如果值比原栈低,则原栈高出的部分舍弃。
// 所以可以用lua_settop(0)来清空栈。
void  lua_settop (lua_State *L, int idx);	
void  lua_pushvalue (lua_State *L, int idx);    //将idx索引上的值的副本压入栈顶
void  lua_remove (lua_State *L, int idx);	//移除idx索引上的值
void  lua_insert (lua_State *L, int idx);	//弹出栈顶元素,并插入索引idx位置
void  lua_replace (lua_State *L, int idx);	//弹出栈顶元素,并替换索引idx位置的值
// 确保堆栈上至少有 n 个额外空位。 如果不能把堆栈扩展到相应的尺寸,
// 函数返回假。 失败的原因包括将把栈扩展到比固定最大尺寸还大 (至少是几
// 千个元素)或分配内存失败。 这个函数永远不会缩小堆栈; 如果堆栈已经
// 比需要的大了,那么就保持原样
int   lua_checkstack (lua_State *L, int n); 

下面就分两个主要部分进行介绍(C++和栈操作;以及Lua和栈操作)

2.C++和栈之间操作相关函数

①c -> stack 将C++数据压到栈里函数:lua_pushxxx

LUA_API void        (lua_pushnil) (lua_State *L);
LUA_API void        (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void        (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void  (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void  (lua_pushboolean) (lua_State *L, int b);
LUA_API void  (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int   (lua_pushthread) (lua_State *L);

②stack -> c 判断栈里类型相关函数: lua_isxxx(lua_State *L, int idx)

LUA_API int             (lua_isnumber) (lua_State *L, int idx);
LUA_API int             (lua_isstring) (lua_State *L, int idx);
LUA_API int             (lua_iscfunction) (lua_State *L, int idx);
LUA_API int             (lua_isinteger) (lua_State *L, int idx);
LUA_API int             (lua_isuserdata) (lua_State *L, int idx);
LUA_API int             (lua_type) (lua_State *L, int idx);

③stack -> c 获取栈里数据相关函数:lua_toxxx (lua_State *L, int idx)

LUA_API lua_Number      (lua_tonumberx) (lua_State *L, int idx, int *isnum);
LUA_API lua_Integer     (lua_tointegerx) (lua_State *L, int idx, int *isnum);
LUA_API int             (lua_toboolean) (lua_State *L, int idx);
LUA_API const char     *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t          (lua_rawlen) (lua_State *L, int idx);
LUA_API lua_CFunction   (lua_tocfunction) (lua_State *L, int idx);
LUA_API void           *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State      *(lua_tothread) (lua_State *L, int idx);
LUA_API const void     *(lua_topointer) (lua_State *L, int idx);

3.Lua和栈之间的操作相关函数

①从Lua中得到数据放到栈里进行操作:lua_getxxx

LUA_API int (lua_getglobal) (lua_State *L, const char *name);
LUA_API int (lua_gettable) (lua_State *L, int idx);
LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawget) (lua_State *L, int idx);
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);LUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int   (lua_getmetatable) (lua_State *L, int objindex);
LUA_API int  (lua_getuservalue) (lua_State *L, int idx);

②从栈里将数据写入到Lua中:lua_setxxx

LUA_API void  (lua_setglobal) (lua_State *L, const char *name);
LUA_API void  (lua_settable) (lua_State *L, int idx);
LUA_API void  (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void  (lua_seti) (lua_State *L, int idx, lua_Integer n);
LUA_API void  (lua_rawset) (lua_State *L, int idx);
LUA_API void  (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
LUA_API void  (lua_rawsetp) (lua_State *L, int idx, const void *p);
LUA_API int   (lua_setmetatable) (lua_State *L, int objindex);
LUA_API void  (lua_setuservalue) (lua_State *L, int idx);

最后总结一下

  1. Lua和C++是通过一个虚拟栈来交互通信的。

  2. C++调用Lua: 由C++先把函数名、变量名、table中键放入栈中,然后把函数名、变量名、table中键出栈,并返回对应的值到栈顶,再由栈顶返回C++。

  3. Lua调C++:

**Lua调用C++的函数:**先把普通的C++函数包装成Lua_CFunction格式,然后注册函数到Lua解释器中,然后由Lua去调用这个模块的函数。

**Lua以面向对象的方式调用C++的类:**新建一个元表metatable,并设置元表里key为"__index"的值的为metatable本身,然后将成员操作方法添加到元表metatable里,在创建对象函数中,把元表赋值给对象指针,这样通过":"操作符就可以找到对应的方法了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/23242.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++实现Qt对象树机制

文章目录 对象树是什么使用对象树的好处使用c实现对象树 对象树是什么 我们常常听到 QObject 会用对象树来组织管理自己&#xff0c;那什么是对象树&#xff1f;  这个概念非常好理解。因为 QObject 类就有一个私有变量 QList<QObject *>&#xff0c;专门存储这个类的子…

上海亚商投顾:沪指震荡微涨 金融、地产午后大幅走强

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪 三大指数早盘震荡&#xff0c;午后集体拉升反弹&#xff0c;创业板指涨超1%。券商等大金融板块午后再度走强&#…

问题解决方案

前端开发 1、npm安装的时候老是卡住 reify:rxjs: timing reifyNode:node_modules/vue/cli/node_modules 查看当前使用的那个镜像 nrm lsnpm ---------- https://registry.npmjs.org/yarn --------- https://registry.yarnpkg.com/cnpm --------- https://r.cnpmjs.org/taobao …

力扣 343. 整数拆分

题目来源&#xff1a;https://leetcode.cn/problems/integer-break/description/ C题解1&#xff1a;动态规划。dp[i] 代表数字i拆分后得到的最大乘积。递归公式为拆分后两个数的最大乘积相乘&#xff0c;即 dp[i] max(dp[i], dp[j] * dp[i-j])。对于n2或3需要另外讨论。 cla…

Scratch 教程:如何实现文本分割

在平时&#xff0c;我们通常会有分割文本的要求&#xff0c;但扩展却又无法使用scratch离线版打开&#xff0c;咋办呢&#xff1f;我们可以用原版做出来&#xff01; 没关系&#xff0c;我来教你&#xff01; 我们自定义一个函数&#xff0c;之后要分割调用就行了 创建三个变量…

【bug】记录一次使用Swiper插件时loop属性和slidersPerView属性冲突问题

简言 最近在vue3使用swiper时&#xff0c;突然发现loop属性和slides-per-view属性同时存在启用时&#xff0c;loop生效&#xff0c;下一步只能生效一次的bug&#xff0c;上一步却是好的。非常滴奇怪。 解决过程 分析属性是否使用错误。 loop是循环模式&#xff0c;布尔型。 …

8.1 配置环境/Linux进程管理总结/Argument/saveload Module/切片

文章目录 一、配置环境二、Linux 进程管理总结三、ArgumentParser四、Saving and Loading Models nn.ModulesWarmstarting Model Using Parameters from a Different Model五、切片&#xff01;拓展&#xff1a; 一、配置环境 github配置环境可以直接赋值到txt中&#xff0c;然…

修复 Adob​​e After Effects 预览无法工作/播放的方法技巧

Adobe After Effects 允许您预览视频和音频&#xff0c;而无需将其渲染为最终输出。当您无法在此应用程序中预览视频和音频时&#xff0c;一定会感到沮丧。不过不用担心&#xff0c;您可以尝试以下方法来修复 After Effects 预览不起作用的问题。 技巧1&#xff1a;重启After …

2023年 Java 面试八股文(20w字)

目录 第一章-Java基础篇 1、你是怎样理解OOP面向对象 难度系数&#xff1a;⭐ 2、重载与重写区别 难度系数&#xff1a;⭐ 3、接口与抽象类的区别 难度系数&#xff1a;⭐ 4、深拷贝与浅拷贝的理解 难度系数&#xff1a;⭐ 5、sleep和wait区别 难度系数&a…

Mac电脑怎么使用“磁盘工具”修复磁盘

我们可以使用“磁盘工具”的“急救”功能来查找和修复磁盘错误。 “磁盘工具”可以查找和修复与 Mac 磁盘的格式及目录结构有关的错误。使用 Mac 时&#xff0c;错误可能会导致意外行为&#xff0c;而重大错误甚至可能会导致 Mac 彻底无法启动。 继续之前&#xff0c;请确保您…

DP-GAN-判别器代码

将输出的rgb作为输入&#xff0c;输入到判别器中。接着执行一个for循环&#xff0c;看一下body_down列表的组成和x经过body_down之后的值。 body_down是由残差块D组成的列表&#xff1a; 残差块的参数为&#xff1a;(3,128),(128,128),(128,256),(256,256),(256,512),(512,5…

64 # 实现一个 http-server

准备工作 上一节实现了通过 commander 的配置获取到用户的参数&#xff0c;下面完成借用 promise 写成类的方法一节没有完成的任务&#xff0c;实现一个 http-server&#xff0c;https://www.npmjs.com/package/http-server&#xff0c;http-server 是一个简单的零配置命令行静…

价值 1k 嵌入式面试题-计算机网络 OSI

开门见山 请讲下 OSI 各层协议的主要功能&#xff1f; 常见问题 回答不系统回答不确切无法和实际网络协议做关联对应 答题思路 OSI 代表了开放互联系统中信息从一台计算机的一个软件应用流到另一个计算机的另一个软件应用的参考模型 OSI 包含 7 层&#xff0c;每一层负责特…

java中使用Jsoup和Itext实现将html转换为PDF

1.在build.gradle中安装所需依赖&#xff1a; implementation group: com.itextpdf, name: itextpdf, version: 5.5.13 implementation group: com.itextpdf.tool, name: xmlworker, version: 5.5.13 implementation group: org.jsoup, name: jsoup, version: 1.15.32.创建工具…

网络安全进阶学习第十课——MySQL手工注入

文章目录 一、MYSQL数据库常用函数二、MYSQL默认的4个系统数据库以及重点库和表三、判断数据库类型四、联合查询注入1、具体步骤&#xff08;靶场演示&#xff09;&#xff1a;1&#xff09;首先判断注入点2&#xff09;判断是数字型还是字符型3&#xff09;要判断注入点的列数…

selenium官网文档阅读总结(day 2)

1.selenium元素定位方法 1.1selenium命令 当我们使用chormdriver打开网页后&#xff0c;接下来就要用python操作元素&#xff0c;模拟用户会作出的操作&#xff0c;这些操作元素的方法就是命令。比如 (1) click&#xff1a;点击&#xff08;按钮&#xff0c;单选框&#xff…

2024年浙师大MBA项目招生信息全面了解

2024年全国管理类硕士联考备考已经到了最火热的阶段&#xff0c;不少考生开始持续将注意力集中在备考的规划中&#xff01;杭州达立易考教育整合浙江省内的MBA项目信息&#xff0c;为大家详细梳理了相关报考参考内容&#xff0c;方便大家更好完成择校以及针对性的备考工作。本期…

为什么list.sort()比Stream().sorted()更快?

真的更好吗&#xff1f; 先简单写个demo List<Integer> userList new ArrayList<>();Random rand new Random();for (int i 0; i < 10000 ; i) {userList.add(rand.nextInt(1000));}List<Integer> userList2 new ArrayList<>();userList2.add…

使用 RediSearch 在 Redis 中进行全文检索

原文链接&#xff1a; 使用 RediSearch 在 Redis 中进行全文检索 Redis 大家肯定都不陌生了&#xff0c;作为一种快速、高性能的键值存储数据库&#xff0c;广泛应用于缓存、队列、会话存储等方面。 然而&#xff0c;Redis 在原生状态下并不支持全文检索功能&#xff0c;这使…

备战秋招 | 笔试强训24

目录 一、选择题 二、编程题 三、选择题题解 四、编程题题解 一、选择题 1、请指出选择排序&#xff0c;冒泡排序&#xff0c;快速排序的时间复杂度分别是&#xff08;&#xff09; A. O(n^2)、O(n^2)、O(n*log2n) B. O(n*log2n)、、O(n^2)、O(n*log2n&#xff09; C. O(n…