当前位置: 首页 > news >正文

Lua与C++交互

文章目录

  • 1、Lua和C++交互
  • 2、基础练习
    • 2.1、加载Lua脚本并传递参数
    • 2.2、加载脚本到stable(包)
    • 2.3、Lua调用c语言接口
    • 2.4、Lua实现面向对象
    • 2.5、向脚本中注册c++的类

1、Lua和C++交互

1、lua和c++交互机制是基于一个虚拟栈,C++和lua之间的所有数据交互都通过这个虚拟栈来完成,无论何时C++想从lua中调用一个值,被请求的值将会被压入栈,C++想要传递一个值给Lua,首选将整个值压栈,然后就可以在Lua中调用。
2、lua中提供正向和反向索引,区别在于证书永远是栈底,负数永远是栈顶。

在这里插入图片描述

2、基础练习

编译指令:g++ test.cpp -o test -llua -ldl

#include <iostream>  
#include <string.h>  
using namespace std;extern "C"
{
#include "lua.h"  
#include "lauxlib.h"  
#include "lualib.h"  
}// g++ test.cpp -o test  -llua -ldl
int main()
{//1.创建一个state  // luaL_newstate返回一个指向堆栈的指针lua_State *L = luaL_newstate();//2.入栈操作  lua_pushstring(L, "hello world");lua_pushnumber(L, 200);//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);return 0;
}

在这里插入图片描述

2.1、加载Lua脚本并传递参数

编译指令:g++ test.cpp -o test -llua -ldl

函数说明:1、函数用于将Lua脚本加载到Lua虚拟机中并进行编译
luaL_loadbuffer(L,s,sz,n)lua_State *L:Lua状态对象,表示Lua虚拟机的运行实例。const char *buff:指向Lua脚本内容的字符串。size_t sz:Lua脚本内容的长度。const char *name:可选参数,用于给脚本设置一个名称,便于调试和错误消息的输出。返回值:不为0表示有错误2、函数用于调用Lua函数并处理其执行过程中可能发生的错误
lua_pcall(L,n,r,f)lua_State *L:Lua状态对象,表示Lua虚拟机的运行实例。int nargs:传递给Lua函数的参数数量。int nresults:期望的返回值数量。int errfunc:错误处理函数在调用栈中的索引。返回值:不为0表示有错误3、函数用于从全局环境中获取一个全局变量,并将其值压入Lua栈顶
int lua_getglobal(lua_State *L, const char *name)lua_State *L:Lua状态对象,表示Lua虚拟机的运行实例。const char *name:要获取的全局变量的名称。4、函数用于将一个数字(lua_Number类型)压入Lua栈顶
void lua_pushnumber(lua_State *L, lua_Number n)lua_State *L:Lua状态对象,表示Lua虚拟机的运行实例。lua_Number n:要压入栈的数字。

执行流程:
1、加载script脚本加载到lua虚拟机中
2、将脚本中的my_pow函数,压入到栈顶
3、压入my_pow需要的两个参数
4、执行脚本
5、获取脚本中的返回值

#include <cstdio>
#include <cstring>
#include <cmath>
#include <new>
extern "C" {#include <lua.h>#include <lualib.h>#include <lauxlib.h>
}char const *script = R"(
function hello()print('hello world')
endfunction my_pow(x,y)return x^y
end
)";char const *script_1 = R"(pkg.hello()
)";int main()
{/*加载脚本并传递参数*/// 创建lua虚拟机,创建虚拟栈lua_State *state = luaL_newstate();// 打开lua标准库,以便正常使用lua apiluaL_openlibs(state);{// 将lua脚本加载到虚拟机中,并编译auto rst = luaL_loadbuffer(state,script,strlen(script),"hello");// 判断是否加载成功if(rst !=0 ){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("load script faile:%s\n",msg);lua_pop(state,-1);}return -1;}// 执行加载并编译的Lua脚本if(lua_pcall(state,0,0,0)){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("load script faile:%s",msg);lua_pop(state,-1);}}// 从全局环境中获取一个my_pow函数压入到栈顶lua_getglobal(state,"my_pow");// 判断栈顶是不是一个函数,要是不是表示没有找到if(!lua_isfunction(state,-1)){printf("function  named my_pow not function\n");return -1;}// 将数字参数压入Lua栈中lua_pushnumber(state,2);lua_pushnumber(state,8);rst = lua_pcall(state,2,1,0);if(rst !=0 ){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("load script faile:%s\n",msg);lua_pop(state,-1);}return -1;}if(lua_isnumber(state,-1)){lua_Number val = lua_tonumber(state,-1);printf("%lf\n",val);}}lua_close(state);return 0;
}

2.2、加载脚本到stable(包)

编译命令: g++ main.cpp -o main -llua -ldl

#include <cstdio>
#include <cstring>
#include <cmath>
#include <new>
extern "C" {#include <lua.h>#include <lualib.h>#include <lauxlib.h>
}
char const *script = R"(
function hello()print('hello world')
endfunction my_pow(x,y)return x^y
end
)";/*   _G = {"helloworld" = function print("hello world")}_G = {"pkg" = {"helloworld" = function print("hello world")}}pkg.helloworld()
*/char const *script_1 = R"(pkg.hello()
)";int main()
{/*加载脚本到stable(包)1、生成chunk push到栈顶2、创建table,设置给_G表,_G["pkg"] = {}3、给这个table设置元表,元表继承_G的访问域(__index)4、执行code chunk*/lua_State *state = luaL_newstate();luaL_openlibs(state);{auto rst = luaL_loadbuffer(state,script,strlen(script),"helloworld");if(rst != 0){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("load script faile:%s\n",msg);lua_pop(state,1);}return -1;}// 取出_G表lua_getglobal(state,"_G");if(lua_istable(state,-1)){  // chunk _Glua_newtable(state);    // 创建表 chunk _G new_stablelua_pushstring(state,"pkg"); // chunk _G new_stable pkglua_pushvalue(state,-2); // chunk _G new_stable pkg new_stablelua_rawset(state,-4);   // chunk _G new_stablechar const *upvalueName = lua_setupvalue(state,-3,1); // chunk _Glua_newtable(state);    // chunk _G metastablelua_pushstring(state,"__index");    // chunk _G metastable "__index"lua_pushvalue(state,-3); // chunk _G metastable "__index" _Glua_rawset(state,-3);   // chunk _G metastablelua_pushstring(state,"pkg");lua_rawget(state,-3);   // chunk _G metastable "pkg"(table)lua_pushvalue(state,-2);    // chunk _G metastable pkg(table) metastablelua_setmetatable(state,-2); // chunk _G metastable pkg(stable)lua_pop(state,3);   // chunk}// 执行chunkif(lua_pcall(state,0,0,0)){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("call function chunk failed:%s\n",msg);lua_pop(state,1);}}// 加载script_1rst = luaL_loadbuffer(state,script_1,strlen(script_1),"script_1");if(rst != 0){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("load script failed:%s\n",msg);lua_pop(state,1);}return -1;}if(lua_pcall(state,0,0,0)){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("call function chunk failed:%s\n",msg);lua_pop(state,1);}}lua_close(state);}return 0;
}

2.3、Lua调用c语言接口

#include <cstdio>
#include <cstring>
#include <cmath>
#include <new>
extern "C" {#include <lua.h>#include <lualib.h>#include <lauxlib.h>
}int pow_from_c(lua_State *L)
{int param_count = lua_gettop(L);if(param_count != 2)return 0;if(lua_isinteger(L,1) && lua_isinteger(L,2)){auto x = lua_tointeger(L,1);auto y = lua_tointeger(L,2);int rst = (int)pow(x,y);lua_pushinteger(L,rst);return 1;}return 0;
}char const *script_2 = R"(local val = pow_from_c(2,3)print(val)
)";
int main()
{// lua调用c语言接口lua_State *state = luaL_newstate();luaL_openlibs(state);{/*"_G" = {"pow_from_c" = pow_from_c}*/lua_getglobal(state,"_G");lua_pushstring(state,"pow_from_c");lua_pushcclosure(state,pow_from_c,0);    // _G "pow_from_c"; closurelua_rawset(state,-3);   // _Glua_pop(state,1);   // _G}auto rst = luaL_loadbuffer(state,script_2,strlen(script_2),"script_2");if(rst != 0){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("load script faile:%s\n",msg);lua_pop(state,1);}return -1;}if(lua_pcall(state,0,0,0)){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("call function chunk failed:%s\n",msg);lua_pop(state,1);}}lua_close(state);return 0;
}

2.4、Lua实现面向对象

local anial_matestable = {__index = {walk = function (self)print(self,"我是walk")end,eat = function (self)print(self,"eat.")end,},__newindex = function (object,key,value)print("assigned "..value.."named "..key.."but not really")end,
}function newobject()local objs = {name = "xxxx"}setmetatable(objs,anial_matestable)return objs
endlocal obj = newobject()
obj.eat()
obj.walk()
obj.name = "abc"
obj.id = 0

2.5、向脚本中注册c++的类

#include <cstdio>
#include <cstring>
#include <cmath>
#include <new>
extern "C" {#include <lua.h>#include <lualib.h>#include <lauxlib.h>
}
char const *script_3 = R"(local obj_1 = create_game_object(1);local obj_2 = create_game_object(1);local obj_3 = create_game_object(2);local rst1 = obj_1:equal(obj_2)local rst2 = obj_1:equal(obj_3)print(rst1,";",rst2)print(""..obj_1:id())
)";class GameObject{
private:u_int32_t _id;
public:static size_t registy_value;
public:GameObject(u_int32_t id):_id(id){}u_int32_t id()const{return _id;}bool equal(GameObject *obj){return _id == obj->id();}
};
size_t GameObject::registy_value = 0;int GameObject_equal(lua_State *state){int arg_count = lua_gettop(state);if(arg_count!=2){return 0;}if(lua_isuserdata(state,1) && lua_isuserdata(state,2)){void *userdata_self = lua_touserdata(state,1);void *userdata_that = lua_touserdata(state,2);GameObject *obj1 = (GameObject*)userdata_self;GameObject *obj2 = (GameObject*)userdata_that;auto rst = obj1->equal(obj2);lua_pushboolean(state,rst);return 1;}return 0;
}int GameObject_id(lua_State* state){GameObject *this_obj = (GameObject*)lua_touserdata(state,1);auto rst = this_obj->id();lua_pushinteger(state,rst);return 1;
}int create_game_object(lua_State* state){auto id = lua_tointeger(state,1);void *p = lua_newuserdata(state,sizeof(GameObject));GameObject *obj = new(p)GameObject(id);lua_rawgetp(state,LUA_REGISTRYINDEX,&GameObject::registy_value);lua_setmetatable(state,-2);return 1;
}int main()
{// 怎么向脚本中注册c++的类// 使用userdata/*userdata:{metadata:{__index = {equal = function(){},id = function(){},}}}*/lua_State *state = luaL_newstate();luaL_openlibs(state);{lua_getglobal(state,"_G");lua_pushstring(state,"create_game_object");lua_pushcclosure(state,create_game_object,0);lua_rawset(state,-3);lua_pop(state,1);lua_newtable(state);lua_pushstring(state,"__index");lua_newtable(state);lua_pushstring(state,"equal");lua_pushcclosure(state,GameObject_equal,0);lua_rawset(state,-3);lua_pushstring(state,"id");lua_pushcclosure(state,GameObject_id,0);lua_rawset(state,-3);lua_rawset(state,-3);lua_rawsetp(state,LUA_REGISTRYINDEX,&GameObject::registy_value);auto rst = luaL_loadbuffer(state,script_3,strlen(script_3),"oop");if(rst != 0){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("load script failed:%s\n",msg);lua_pop(state,1);}return -1;}// 执行if(lua_pcall(state,0,0,0)){if(lua_isstring(state,-1)){auto msg = lua_tostring(state,-1);printf("load script failed:%s\n",msg);lua_pop(state,1);}}}lua_close(state);return 0;
}

相关文章:

Lua与C++交互

文章目录 1、Lua和C交互2、基础练习2.1、加载Lua脚本并传递参数2.2、加载脚本到stable&#xff08;包&#xff09;2.3、Lua调用c语言接口2.4、Lua实现面向对象2.5、向脚本中注册c的类 1、Lua和C交互 1、lua和c交互机制是基于一个虚拟栈&#xff0c;C和lua之间的所有数据交互都通…...

Ubuntu安装pyenv,配置虚拟环境

文章目录 安装pyenvpyenv创建虚拟环境一般情况下创建虚拟环境的方法 安装pyenv 摘自&#xff1a;文章 pyenv可以管理不同的python版本 1、安装pyenv的依赖库 # 执行以下命令安装依赖库 # 更新源 sudo apt-get update # 更新软件 sudo apt-get upgradesudo apt-get install ma…...

【分布式技术专题】「分布式技术架构」MySQL数据同步到Elasticsearch之N种方案解析,实现高效数据同步

MySQL数据同步到Elasticsearch之N种方案解析&#xff0c;实现高效数据同步 前提介绍MySQL和ElasticSearch的同步双写优点缺点针对于缺点补充优化方案 MySQL和ElasticSearch的异步双写优点缺点 定时延时写入ElasticSearch数据库机制优点缺点 开源和成熟的数据迁移工具选型Logsta…...

什么是React中的高阶组件(Higher Order Component,HOC)?它的作用是什么?

聚沙成塔每天进步一点点 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发感兴趣、刚刚踏入前端领域的朋友们量身打造的。无论你是完全的新手还是有一些基础的开发…...

NEFU离散数学实验3-递推方程

相关概念 递推方程是指一种递归定义&#xff0c;它将问题拆分成更小的子问题&#xff0c;并使用这些子问题的解来计算原问题的解。离散数学中&#xff0c;递推方程通常用于描述数列、组合问题等。 以下是一些递推方程相关的概念和公式&#xff1a; 1. 递推公式&#xff1a;递推…...

如何为你的地图数据设置地图样式?

地图样式设置是GIS系统中非常重要的功能模块&#xff0c;水经微图Web版本最近对符号样式功能模块进行了升级。 你可以通过以下网址直接打开访问&#xff1a; https://map.wemapgis.com 现在我们为大家分享一下水经微图Web版中&#xff0c;如何为你标注的地图数据设置地图样式…...

解决visual studio Just-In-Time Debugger调试

解决visual studio Just-In-Time Debugger调试 网上流行很多方法&#xff0c;最后一直不行&#xff0c;其实有最简单的方法比较实用 方法一&#xff1a;把 C:\WINDOWS\system32\vsjitdebugger.exe,删除了&#xff0c;若怕出问题&#xff0c;可以把它改名或者做个rar文件暂时保留…...

Uservue 中 keep-alive 组件的作用

目录 前言 用法 代码 理解 keep-alive 是 Vue.js 中一个内置的组件&#xff0c;它能够将不活动的组件实例保存在内存中&#xff0c;防止其被销毁&#xff0c;以便在后续需要时能够快速重新渲染。这个功能在一些需要频繁切换但不希望每次都重新渲染的场景中非常有用&#xf…...

gitlab查看、修改用户和邮箱,gitlab生成密钥

查看用户、邮箱 git config user.name git config user.email 修改用户、邮箱 git config --global user.name “xxx” git config --global user.email “xxxxxx.com” 生成ssh密钥 ssh-keygen -t rsa -C “xxxxxx.com” 查看SSH秘钥 cat ~/.ssh/id_rsa.pub 将秘钥复制&…...

python操作MySQL、SQL注入问题、视图、触发器、事务、存储过程、函数、流程控制、索引(重点)

python操作MySQL(重要) SQL的由来&#xff1a; MySQL本身就是一款C/S架构&#xff0c;有服务端、有客户端&#xff0c;自身带了有客户端&#xff1a;mysql.exe python这门语言成为了MySQL的客户端(对于一个服务端来说&#xff0c;客户端可以有很多) 操作步骤&#xff1a; …...

这一年的资源

#线性代数 https://textbooks.math.gatech.edu/ila/one-to-one-onto.html行业规范https://xlinux.nist.gov/dads/https://www.dhs.gov/publications产业群链基金会 https://www.cncf.io/谷歌 https://opensource.google/projects网飞 高德纳 https://www.gartne…...

从【臀部监控】到【电脑监控软件】,企业如何在隐私权与管理权博弈中找到平衡

【臀部监控】 依稀记得在2021年初某个高科技产品的爆火&#xff0c;惹得各大媒体网站争相报道。 起因是一位杭州网友在论坛上发帖&#xff0c;不久前公司给员工发放了一批高科技坐垫。 这个坐垫能自动感应心跳、呼吸在内的诸多人体数据&#xff0c;还能提醒人保持正确坐姿以及…...

数据库简介和sqlite3安装

数据库就是存储数据的仓库&#xff0c;其本质是一个文件系统&#xff0c;数据按照特定的格式将数据存储起来&#xff0c;用户可以对数据库中的数据进行增加&#xff0c;修改&#xff0c;删除及查询操作。 严格意义上来说,"数据库"不能被称之为"数据库",而…...

颈肩肌筋膜炎做什么检查

颈肩肌筋膜炎症状 颈肩背部广泛疼痛酸胀沉重感、麻木感&#xff0c;僵硬、活动受限&#xff0c;可向后头部及上臂放散。疼痛呈持续性&#xff0c;可因感染、疲劳、受凉、受潮等因素而加重。查体见颈部肌紧张&#xff0c;压痛点常在棘突及棘突旁斜方肌、菱形肌等&#xff0c;压…...

django建站过程(3)定义模型与管理页

定义模型与管理页 定义模型[models.py]迁移模型向管理注册模型[admin.py]注册模型使用Admin.site.register(模型名)修改Django后台管理的名称定义管理列表页面应用名称修改管理列表添加查询功能 django shell交互式shell会话 认证和授权 定义模型[models.py] 模仿博客形式&…...

node开发微信群聊机器人第⑤章

▍PART 序 看本文时&#xff0c;请确保前4章都已经看过&#xff0c;不然本章你看着看着思维容易跳脱&#xff01;再一个机器人教程只在公众号&#xff1a;“程序员野区”首发。csdn会跟着发一份&#xff0c;未经博主同意&#xff0c;请勿转载&#xff01;欢迎分享到自己的微信…...

如何助力企业出海?未来发展趋势是什么?尽在「云通信」Tech专场

2023杭州云栖大会 倒计时4天&#xff01; 阿里云云通信 2大并行Session 6场话题演讲 今日「云通信」Tech 议程内容抢先知晓 01 「云通信」Tech • 国内企业出海&#xff0c;如何更高地提升市场营销的ROI&#xff0c;提升客户的转化率&#xff1f; • 面对海外存量客户&a…...

安装虚拟机(VMware)保姆级教程及配置虚拟网络编辑器和安装WindowsServer以及本地访问虚拟机和配置服务器环境

目录 一、操作系统 1.1.什么是操作系统 1.2.常见操作系统 1.3.个人版本和服务器版本的区别 1.4.Linux的各个版本 二、VMware Wworkstation Pro虚拟机的安装 1.下载与安装 注意&#xff1a;VMWare虚拟网卡 2.配置虚拟网络编辑器 三、安装配置 WindowsServer 1.创建虚拟…...

使用Typecho搭建个人博客网站,并内网穿透实现公网访问

使用Typecho搭建个人博客网站&#xff0c;并内网穿透实现公网访问 文章目录 使用Typecho搭建个人博客网站&#xff0c;并内网穿透实现公网访问前言1. 安装环境2. 下载Typecho3. 创建站点4. 访问Typecho5. 安装cpolar6. 远程访问Typecho7. 固定远程访问地址8. 配置typecho 前言 …...

RabbitMQ (4)

RabbitMQ (4) 文章目录 1. 死信的概念2. 死信的来源3. 死信代码案例3.1 TTL 过期时间3.2 超过队列最大长度3.3 拒绝消息 前言   上文我们已经学习完 交换机 &#xff0c;知道了几个交换机的使用 &#xff0c;下面我们来学习一下 死信队列 1. 死信的概念 先从概念解释上搞清楚这…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

Mac flutter环境搭建

一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...

__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.

这个警告表明您在使用Vue的esm-bundler构建版本时&#xff0c;未明确定义编译时特性标志。以下是详细解释和解决方案&#xff1a; ‌问题原因‌&#xff1a; 该标志是Vue 3.4引入的编译时特性标志&#xff0c;用于控制生产环境下SSR水合不匹配错误的详细报告1使用esm-bundler…...

Neo4j 完全指南:从入门到精通

第1章&#xff1a;Neo4j简介与图数据库基础 1.1 图数据库概述 传统关系型数据库与图数据库的对比图数据库的核心优势图数据库的应用场景 1.2 Neo4j的发展历史 Neo4j的起源与演进Neo4j的版本迭代Neo4j在图数据库领域的地位 1.3 图数据库的基本概念 节点(Node)与关系(Relat…...