欢迎访问Lu程序设计

C/C++注册动态对象到Lu系统并进行运算符重载

1 说明

要演示本文的例子,你必须下载Lu32脚本系统。本文的例子需要lu32.dll、lu32.lib、C格式的头文件lu32.h,相信你会找到并正确使用这几个文件。

用C/C++编译器创建一个控制台应用程序,复制本文的例子代码直接编译运行即可。

2 关于运算符重载

在本教程系列的开始,介绍了Lu脚本的基本数据结构(详细参考Lu编程指南),即:

struct LuData{    //Lu基本数据结构。
luIFOR x; //luIFOR被定义为64位整数__int64,用于存放数据。对于动态数据类型,对象指针约定保存在x的前4个字节中。
luIFOR y; //存放数据。
luIFOR z; //存放数据。
luKEY VType; //luKEY被定义为32位整数__int32。扩展数据类型,决定重载函数,从而决定了对数据的操作方式。
luKEY BType; //基本数据类型,决定了Lu数据的结构。
};

基本数据类型BType决定了实际的数据结构,而扩展数据类型VType决定了重载函数。若要对某数据类型VType进行运算符重载,需要用函数LockKey对VType加锁,该函数定义如下:

int _stdcall LockKey(luKEY VType,void (_stdcall *DeleteKey)(void *),luOperator OpLock);

VType:被锁定的键的类型。VType>luPubKey_User(公有键、普通键)或者VType<luPriKey_User(私有键)。
    DeleteKey:删除键值的函数指针,用于标识要加锁的键。该函数由用户定义,但由Lu调用。若DeleteKey=NULL,表示解锁指定的键。
    OpLock:luOperator类型的函数指针,用于对象(用指针标识)的运算符重载,该参数不可为NULL。解锁和加锁所用的OpLock函数必须相同。参考[注1]

如果加锁或解锁成功,该函数返回0,否则返回非0值。

[注1]:运算符重载函数luOperator函数格式如下(与Lu二级函数相比,仅多了一个参数theOperator):

//m指出数组Para的参数个数(也即操作数的个数,0表示1个,1表示2个,以此类推)。
//hFor为调用该函数的表达式句柄(与二级函数中的表达式句柄相同)。
//theOperator指出运算符的类型或操作类型:+、-、*、/、^、... ...。
LuData (_stdcall *luOperator)(luINT m,LuData *Para,void *hFor,int theOperator); LuData _stdcall OpLock(luINT m,LuData *Para,void *hFor,int theOperator)
{
//... ...
switch(theOperator)
{
case 0: //重载运算符+
//... ...
case 1: //重载运算符-
//... ...
case 2: //重载运算符*
//... ...
case 3: //重载运算符%
//... ...
case 4: //重载运算符/
//... ...
... ...
}
}

如果不打算给加锁的键提供运算符或函数重载功能,须使用函数SetRunErr向Lu报告运行错误。

本文讨论C/C++注册动态对象到Lu系统并进行运算符重载。本文的例子是实现三维向量及部分相关运算(基本类型和扩展类型均为 key_Vector)。实际上,Lu脚本中内置了三维向量(vector)的运算,例如:

(1$2$3) * (7$6$5)

结果为:

(-8.,16.,-8.)

Lu脚本内置的三维向量(vector)是静态数据,但本文的三维向量(Vector)被定义为动态数据,仅为了演示如何将动态对象注册到Lu系统并进行运算符重载。同时,本文还演示了二级函数如何返回一个动态对象;演示了二级函数如何通过参数返回多个动态对象;演示了重载函数new、oset、oget、o的用法;演示了如何由字符串获得一个唯一的整数,以及该整数在Lu脚本中的用法;演示了如何在Lu系统中注册常量和函数;演示了如何接收Lu系统的字符串信息等等。

由于演示的项目较多,本文的例子代码较长。

3 代码

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <locale.h>
#include "lu32.h" #pragma comment( lib, "lu32.lib" ) luKEY key_Vector = luPriKey_User-20; //标识Vector对象的私有键,将对其加锁 //在Lu脚本中,对象成员一般用#开头的字符串标识,例如 #name
luVOID Vector_x; //标识Vector的成员x,在Lu脚本中,设a是一个Vector,则成员x的表示为 a.#x
luVOID Vector_y; //标识Vector的成员y,在Lu脚本中,设a是一个Vector,则成员y的表示为 a.#y
luVOID Vector_z; //标识Vector的成员z,在Lu脚本中,设a是一个Vector,则成员z的表示为 a.#z typedef struct lu_Vector //Vector结构定义,将注册为私有键
{
double x;
double y;
double z;
} lu_Vector; void _stdcall Del_Vector(void *me) //销毁Vector的函数,将注册到Lu系统,实现自动销毁Vector对象
{
free(me);
} void _stdcall LuMessage(wchar_t *pch) //输出动态库信息,该函数注册到Lu,由Lu二级函数调用
{
wprintf(L"%s",pch);
} //定义Lu脚本可调用的二级函数 //演示二级函数如何返回一个动态对象,通过运算符*重载调用
LuData _stdcall Vector_mul(luINT mm,LuData *xx,void *vFor) //计算两个Vector的积,返回一个新Vector
{
static wchar_t ErrName[]=L"Vector *";
lu_Vector *pVector0,*pVector1,*pVector2;
void *NowKey=NULL; //为避免编译器发出警告进行初始化,实际上不需要
char keyname[sizeof(luVOID)];
LuData a; if(xx->BType==key_Vector && (xx+1)->BType==key_Vector)
{
pVector1=(lu_Vector *)SearchKey((char *)&(xx[0].x), sizeof(luVOID), key_Vector);
pVector2=(lu_Vector *)SearchKey((char *)&(xx[1].x), sizeof(luVOID), key_Vector);
if(!pVector1 || !pVector2) goto err;
pVector0=(lu_Vector *)GetBufObj(key_Vector,keyname); //先尝试从缓冲区中获取一个Vector对象
if(!pVector0)
{
pVector0=(lu_Vector *)malloc(sizeof(lu_Vector));
if(!pVector0) goto err;
//在Lu键树中注册键值,参数-1表示注册为指针键,下同
if(InsertKey((char *)&pVector0,-1,key_Vector,pVector0,Del_Vector,NULL,0,NowKey))
{
free(pVector0);
goto err;
}
}
pVector0->x = pVector1->y*pVector2->z - pVector1->z*pVector2->y;
pVector0->y = pVector1->z*pVector2->x - pVector1->x*pVector2->z;
pVector0->z = pVector1->x*pVector2->y - pVector1->y*pVector2->x;
FunReObj(vFor); //通知Lu,该函数将返回一个动态对象
a.BType=key_Vector; a.VType=key_Vector; a.x=0; *(luVOID *)&(a.x)=(luVOID)pVector0;
return a;
}
err: //简化的运行错误处理,实用中要区分错误的不同类型,以方便用户查找错误来源,下同
a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;
SetRunErr(1,ErrName,1,0,vFor);
return a;
} //演示二级函数如何通过参数返回多个动态对象,注册到Lu脚本系统
//用法:addsub[a,b,&c,&d],其中a和b是两个Vector对象,c返回a+b,d返回a-b
LuData _stdcall Vector_addsub(luINT mm,LuData *xx,void *vFor) //计算两个Vector的和与差,返回两个新Vector
{
static wchar_t ErrName[]=L"Vector addsub";
lu_Vector *pVector0,*pVector00,*pVector1,*pVector2;
void *NowKey=NULL; //为避免编译器发出警告进行初始化,实际上不需要
char keyname[sizeof(luVOID)];
LuData a; if(xx->BType==key_Vector && (xx+1)->BType==key_Vector)
{
pVector1=(lu_Vector *)SearchKey((char *)&(xx[0].x), sizeof(luVOID), key_Vector);
pVector2=(lu_Vector *)SearchKey((char *)&(xx[1].x), sizeof(luVOID), key_Vector);
if(!pVector1 || !pVector2) goto err;
pVector0=(lu_Vector *)GetBufObj(key_Vector,keyname); //先尝试从缓冲区中获取一个Vector对象
if(!pVector0)
{
pVector0=(lu_Vector *)malloc(sizeof(lu_Vector));
if(!pVector0) goto err;
if(InsertKey((char *)&pVector0,-1,key_Vector,pVector0,Del_Vector,NULL,0,NowKey)) //在Lu键树中注册键值
{
free(pVector0);
goto err;
}
}
pVector00=(lu_Vector *)GetBufObj(key_Vector,keyname); //先尝试从缓冲区中获取一个Vector对象
if(!pVector00)
{
pVector00=(lu_Vector *)malloc(sizeof(lu_Vector));
if(!pVector00)
{
DeleteKey((char *)&pVector0,sizeof(luVOID),key_Vector,Del_Vector,1); //在Lu键树中删除键值,参数1表示先放到缓冲区中
goto err;
}
if(InsertKey((char *)&pVector00,-1,key_Vector,pVector00,Del_Vector,NULL,0,NowKey)) //在Lu键树中注册键值
{
free(pVector0);
DeleteKey((char *)&pVector0,sizeof(luVOID),key_Vector,Del_Vector,1); //在Lu键树中删除键值,参数1表示先放到缓冲区中
goto err;
}
}
pVector0->x = pVector1->x + pVector2->x;
pVector0->y = pVector1->y + pVector2->y;
pVector0->z = pVector1->z + pVector2->z;
pVector00->x = pVector1->x - pVector2->x;
pVector00->y = pVector1->y - pVector2->y;
pVector00->z = pVector1->z - pVector2->z;
FunSaveObj(vFor,NULL,xx); //通知Lu,准备用参数返回动态对象
xx[2].BType=key_Vector; xx[2].VType=key_Vector; xx[2].x=0; *(luVOID *)&(xx[2].x)=(luVOID)pVector0;
FunSaveObj(vFor,xx+2,xx); //通知Lu,参数3返回动态对象:两个Vector的和
xx[3].BType=key_Vector; xx[3].VType=key_Vector; xx[3].x=0; *(luVOID *)&(xx[3].x)=(luVOID)pVector00;
FunSaveObj(vFor,xx+3,xx); //通知Lu,参数4返回动态对象:两个Vector的差
//提示:如果还要通过返回值返回一个动态对象,仍然使用FunReObj函数,本例未通过返回值返回动态对象
return *xx;
}
err:
a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;
SetRunErr(1,ErrName,1,0,vFor);
return a;
} //演示重载函数new的用法,通过函数重载调用。new[Vector]可生成一个Vector动态对象
LuData _stdcall Vector_new(luINT mm,LuData *xx,void *vFor) //生成一个Vector对象
{
static wchar_t ErrName[]=L"Vector new";
lu_Vector *pVector0;
void *NowKey=NULL; //为避免编译器发出警告进行初始化,实际上不需要
char keyname[sizeof(luVOID)];
LuData a; pVector0=(lu_Vector *)GetBufObj(key_Vector,keyname); //先尝试从缓冲区中获取一个Vector对象
if(!pVector0)
{
pVector0=(lu_Vector *)malloc(sizeof(lu_Vector));
if(!pVector0) goto err;
if(InsertKey((char *)&pVector0,-1,key_Vector,pVector0,Del_Vector,NULL,0,NowKey)) //在Lu键树中注册键值
{
free(pVector0);
goto err;
}
}
FunReObj(vFor); //该函数将返回一个动态对象
a.BType=key_Vector; a.VType=key_Vector; a.x=0; *(luVOID *)&(a.x)=(luVOID)pVector0;
return a;
err:
a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0;
SetRunErr(1,ErrName,1,0,vFor);
return a;
} //演示重载函数oset的用法,通过函数重载调用。设a是一个Vector对象,可实现赋值 a.#x=1.1, a.#y=2.2, a.#z=3.3
LuData _stdcall Vector_oset(luINT mm,LuData *xx,void *vFor) //对Vector的元素赋值
{
static wchar_t ErrName[]=L"Vector oset";
lu_Vector *pVector1; if(xx->BType==key_Vector && xx[1].BType==luStaData_struniint && xx[2].BType==luStaData_double)
{
pVector1=(lu_Vector *)SearchKey((char *)&(xx[0].x), sizeof(luVOID), key_Vector);
if(!pVector1) goto err;
if((luVOID)xx[1].x == Vector_x)
{
pVector1->x = *(double *)&(xx[2].x);
}
else if((luVOID)xx[1].x == Vector_y)
{
pVector1->y = *(double *)&(xx[2].x);
}
else if((luVOID)xx[1].x == Vector_z)
{
pVector1->z = *(double *)&(xx[2].x);
}
else
{
goto err;
}
return *xx;
}
err:
SetRunErr(1,ErrName,1,0,vFor);
return *xx;
} //演示重载函数oget的用法,通过函数重载调用。设a是一个Vector对象,可实现取值 a.#x, a.#y, a.#z
LuData _stdcall Vector_oget(luINT mm,LuData *xx,void *vFor) //对Vector的元素赋值
{
static wchar_t ErrName[]=L"Vector oget";
lu_Vector *pVector1;
LuData a; if(xx->BType==key_Vector && xx[1].BType==luStaData_struniint)
{
pVector1=(lu_Vector *)SearchKey((char *)&(xx[0].x), sizeof(luVOID), key_Vector);
if(!pVector1) goto err;
if((luVOID)xx[1].x == Vector_x)
{
*(double *)&(a.x) = pVector1->x;
}
else if((luVOID)xx[1].x == Vector_y)
{
*(double *)&(a.x) = pVector1->y;
}
else if((luVOID)xx[1].x == Vector_z)
{
*(double *)&(a.x) = pVector1->z;
}
else
{
goto err;
}
a.BType=luStaData_double; a.VType=luStaData_double; //返回一个实数
return a;
}
err:
SetRunErr(1,ErrName,1,0,vFor);
return *xx;
} //演示重载函数o的用法,通过函数重载调用。设a是一个Vector对象,o[a]可输出如下信息:Vector x = 1.1, y = 2.3, z = 3.3
LuData _stdcall Vector_o(luINT mm,LuData *xx,void *vFor) //输出Vector的值
{
static wchar_t ErrName[]=L"Vector o";
lu_Vector *pVector1;
wchar_t wchNum[32];
char chNum[32];
luVOID i,k=0;
luMessage pMessage;
LuData a; pMessage=(luMessage)SearchKey((char *)&k,sizeof(luVOID),luPubKey_User);
if(xx->BType==key_Vector)
{
pVector1=(lu_Vector *)SearchKey((char *)&(xx[0].x), sizeof(luVOID), key_Vector);
if(!pVector1) goto err; pMessage(L"Vector x = ");
k = 11; _gcvt_s(chNum,32,pVector1->x,8);
for(i=0;chNum[i];i++) wchNum[i]=chNum[i];
wchNum[i]='\0';
pMessage(wchNum);
k = k + i; pMessage(L", y = ");
k = k + 6; _gcvt_s(chNum,32,pVector1->y,8);
for(i=0;chNum[i];i++) wchNum[i]=chNum[i];
wchNum[i]='\0';
pMessage(wchNum);
k = k + i; pMessage(L", z = ");
k = k + 6; _gcvt_s(chNum,32,pVector1->z,8);
for(i=0;chNum[i];i++) wchNum[i]=chNum[i];
wchNum[i]='\0';
pMessage(wchNum);
k = k + i; pMessage(L" ");
k = k + 2; a.BType=luStaData_int64; a.VType=luStaData_int64; a.x=k; //o函数总是返回输出的字符总数
return a;
}
err:
SetRunErr(1,ErrName,1,0,vFor);
return *xx;
} LuData _stdcall OpLock_Vector(luINT m,LuData *Para,void *hFor,int theOperator) //Vector的运算符重载函数
{
LuData a; switch(theOperator)
{
case 2: //重载运算符*
return Vector_mul(m,Para,hFor);
case 46: //重载函数new
return Vector_new(m-1,Para+1,hFor); //注意参数个数的变化,忽略了new[Vector]中的参数Vector
case 47: //重载函数oset
return Vector_oset(m,Para,hFor);
case 48: //重载函数oget
return Vector_oget(m,Para,hFor);
case 49: //重载函数o
return Vector_o(m,Para,hFor);
default:
a.BType=luStaData_nil; a.VType=luStaData_nil; a.x=0; //没有重载该运算符或者函数,返回nil
SetRunErr(1,L"Vector 无法识别的运算符!",theOperator,0,hFor);
}
return a;
} void main(void)
{
void *hFor; //存放表达式句柄,即脚本函数句柄
luINT nPara; //存放表达式的自变量个数
LuData *pPara; //存放输入自变量的数组指针
luINT ErrBegin,ErrEnd; //表达式编译出错的初始位置和结束位置
int ErrCode; //错误代码
LuData Val; //Lu基本数据类型
void *v=NULL; //为了避免编译器报错,初始化为NULL
luVOID k=0; //32位平台上luVOID被定义为__int32;64位平台上luVOID被定义为__int64;k必须赋值为0
wchar_t ForStr[]=L"main(:a)= a=new[Vector], a.#x=1.1, a.#y=2.2, a.#z=3.3, o[a]"; //字符串表达式 setlocale(LC_ALL, "chs"); //设置可以输出中文 if(!InitLu()) return; //初始化Lu Vector_x = StrToUniInt((char *)L"x",2); //由字符串"x"获得一个唯一的整数,用于标识Vector的成员x
Vector_y = StrToUniInt((char *)L"y",2); //由字符串"y"获得一个唯一的整数,用于标识Vector的成员y
Vector_z = StrToUniInt((char *)L"z",2); //由字符串"z"获得一个唯一的整数,用于标识Vector的成员z Val.BType=luStaData_int64; Val.VType=luStaData_int64; Val.x=key_Vector; //定义整数常量Vector,标识Vector对象
SetConst(L"Vector",&Val); //设置常量 SetFunction(L"addsub",Vector_addsub,3); //设置二级函数addsub,有4个参数 InsertKey((char *)&k,sizeof(luVOID),luPubKey_User,LuMessage,NULL,NULL,1,v);//使Lu运行时可输出函数信息 LockKey(key_Vector,Del_Vector,OpLock_Vector); //在Lu键树中加锁键,只能存储Vector类型 ErrCode=LuCom(ForStr,0,0,0,&hFor,&nPara,&pPara,&ErrBegin,&ErrEnd); //编译表达式
if(ErrCode)
{
printf("表达式有错误!错误代码: %d \n",ErrCode);
}
else
{
LuCal(hFor,pPara); //运行表达式
} LockKey(key_Vector,NULL,OpLock_Vector); //在Lu键树中解锁键 FreeLu(); //释放Lu
}

运行结果:

Vector x = 1.1, y = 2.2, z = 3.3

4函数说明

本例用到了Lu的近一半的输出函数:初始化Lu的函数InitLu,释放Lu的函数FreeLu,编译表达式的函数LuCom、计算表达式的函数LuCal、加锁键函数LockKey、注册常量的函数SetConst、注册C/C++函数的函数SetFunction、由字符串获得一个唯一的整数StrToUniInt、插入键值的函数InsertKey、搜索键值的函数SearchKey、从缓冲区中获取一个对象的函数GetBufObj、从二级函数返回一个动态对象FunReObj、通过二级函数的参数返回多个动态对象FunSaveObj、设置运行错误的函数SetRunErr。从这里查看这些函数的说明:Lu编程指南

5 难点分析

在给自定义动态类型数据添加运算符重载功能时,需要:(1)用LockKey加锁一个键(本例为key_Vector),自定义的动态类型数据使用该键向Lu系统进行注册,并注册为指针键,以便于Lu系统对垃圾对象的管理(若注册为非指针键,需要自己进行管理);(2)定义删除键值的函数(本例为Del_Vector);(3)定义运算符重载函数(本例为OpLock_Vector)。

在用C/C++进行Lu二级函数设计时,勿忘有关注意实现,参考函数SetFunction的说明。另外,为了提高运行效率,应优先使用函数GetBufObj从缓冲池获取一个动态对象。

代码中定义的Lu二级函数,只有Vector_addsub直接注册到了Lu系统,其余的函数Vector_mul、Vector_new、Vector_oset、Vector_oget、Vector_o是通过Vector对象的运算符重载或者函数重载来间接调用的。注意重载函数Vector_o设计时,o函数除了返回对象信息外,还要返回信息字符串的字符个数。

在Lu脚本中,对象成员一般用#开头的字符串标识,例如 #name 。本例对象Vector的的成员也使用这种表示方法,例如,设a是一个Vector对象,可实现赋值 a.#x=1.1, a.#y=2.2, a.#z=3.3 。实现原理很简单:Lu脚本编译器在编译字符串表达式时,如遇到以#开头的字符串(例如 #name),就使用该字符串生成一个在Lu脚本中唯一的整数(该整数在Lu系统运行期间保持不变),故本程序main函数一开始,就用函数StrToUniInt获取了相关字符串对应的整数值,用以标识Vector对象的成员。

(1)如果修改代码中的字符串表达式为(注意C/C++字符串中转义字符 \" 的使用):

main(:a)= a=new[Vector], a.#x=1., a.#y=2., a.#z=3., o[a.#x, " ",a.#y, " ",a.#z, " "]

可得到如下结果:

1. 2. 3.

(2)如果修改代码中的字符串表达式为:

main(:a,b)= a=new[Vector], a.#x=1., a.#y=2., a.#z=3., b=new[Vector], b.#x=7., b.#y=6., b.#z=5., o[a*b]

可得到如下结果:

Vector x = -8., y = 16., z = -8.

(3)如果修改代码中的字符串表达式为:

main(:a,b,c,d)= a=new[Vector], a.#x=1., a.#y=2., a.#z=3., b=new[Vector], b.#x=7., b.#y=6., b.#z=5., addsub[a,b,&c,&d], o[c,d]

可得到如下结果:

Vector x = 8., y = 8., z = 8. Vector x = -6., y = -4., z = -2.

(4)如果修改代码中的字符串表达式为(注意C/C++字符串中转义字符 \" 的使用):

main(:a,b,i,t0)= t0=clock(), a=new[Vector], a.#x=1., a.#y=2., a.#z=3., b=new[Vector], b.#x=1., b.#y=0., b.#z=0., i=0, while{++i<=1000000, a=a*b}, o[a, " time= ",clock()-t0," ms. "]

可得到如下结果:

Vector x = -0., y = 2., z = 3. time= 531 ms.

本例用Lu脚本内置的静态三维向量vcctor实现,代码为(用OpenLu演示):

main(:a,b,i,t0)= t0=clock(), a=1$2$3, b=1$0$0, i=0, while{++i<=1000000, a=a*b}, o[a, " time= ",clock()-t0," ms. "];

结果为:

{(-0.)$(2.)$(3.)} time= 235 ms.

本例的C++实现为:

#include <iostream>
#include <time.h> using namespace std; class Vector
{
public:
double x;
double y;
double z; Vector(double a=0.0, double b=0.0, double c=0.0)
{
x=a; y=b; z=c;
}
};
Vector operator *(Vector &a, Vector &b)
{
return Vector(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x);
}
void main(void)
{
Vector a,b;
int i;
clock_t start, end; start = clock();
a.x=1.0; a.y=2.0; a.z=3.0;
b.x=1.0; b.y=0.0; b.z=0.0;
for(i=0;i<1000000;i++)
{
a=a*b;
}
end = clock();
cout << a.x << " " << a.y << " " << a.z << " time= " << end - start << " ms." << endl;
}

结果为:

-0 2 3 time= 16 ms.

考虑到运算符重载时有新对象生成(例如a*b将生成一个新对象),参考以下C++程序:

#include <iostream>
#include <time.h> using namespace std; class Vector
{
public:
double x;
double y;
double z; Vector(double a=0.0, double b=0.0, double c=0.0)
{
x=a; y=b; z=c;
}
};
Vector operator *(Vector &a, Vector &b)
{
return Vector(a.y*b.z - a.z*b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x);
}
void main(void)
{
Vector *pv;
int i;
clock_t start, end; start = clock();
for(i=0;i<1000000;i++)
{
pv = new Vector;
delete pv;
}
end = clock();
cout << " time= " << clock() - start << " ms." << endl;
}

结果为:

time= 312 ms.

看来,C++对运算符重载也是有优化的。

6 其他

你可能注意到了,我的联系方式就在下面,如有不明之处或有什么建议,可随时与我进行联系。


版权所有© Lu程序设计 2002-2013,保留所有权利
E-mail: forcal@sina.com  QQ:630715621
最近更新: 2014年01月05日

C/C++注册动态对象到Lu系统并进行运算符重载的更多相关文章

  1. C/C++对Lu系统内置动态对象进行运算符重载

    欢迎访问Lu程序设计 C/C++对Lu系统内置动态对象进行运算符重载 1 说明 要演示本文的例子,你必须下载Lu32脚本系统.本文的例子需要lu32.dll.lu32.lib.C格式的头文件lu32. ...

  2. Unity3D 创建动态的立方体图系统

    Unity3D 创建动态的立方体图系统 这一篇主要是利用上一篇的Shader,通过脚本来完成一个动态的立方体图变化系统. 准备工作如下: 创建一个新的场景.一个球体.提供给场景一个平行光,准备2个立方 ...

  3. c#注册表对象映射

    用于快捷保存与读取注册表,为对应的对象 示例 [RegistryRoot(Name = "superAcxxxxx")] public class Abc : IRegistry ...

  4. Oracle 用户、对象权限、系统权限

    --================================ --Oracle 用户.对象权限.系统权限 --================================  一.用户与模式 ...

  5. Oracle笔记之对象权限与系统权限总结

    对象权限与系统权限 创建表和创建session是系统权限: 系统管理员是有权限去訪问其它表的 以sys登录     sqlplus sys/on_change_install as sysdba; 创 ...

  6. 在注册表中查看Windows10系统激活密钥的方法

      1 2 3 4 5 6 7 分步阅读 百度经验:jingyan.baidu.com 激活Windows10系统(非自己使用激活密钥激活的系统)以后,我们不一定清楚激活密钥是什么.如果想查看自己电脑 ...

  7. C#动态对象(dynamic)示例(实现方法和属性的动态)

    C#的动态对象的属性实现比较简单,如果要实现动态语言那种动态方法就比较困难,因为对于dynamic对象,扩展方法,匿名方法都是不能用直接的,这里还是利用对象和委托来模拟这种动态方法的实现,看起来有点J ...

  8. 动态对象创建(二)重载new和delete

    动态对象创建(二)重载new和delete 前言 上文我简单介绍了一下动态对象创建的方法,这一篇文章的内容主要是对重载new和delete做一些讲解,也希望能够得到博友们的指点,在这里谢过大家. 通常 ...

  9. 动态标绘演示系统1.4.3(for ArcGIS Flex)

    标绘有API文档啦! 在线浏览 ------------------------------------------------------------------------------------ ...

随机推荐

  1. Timeout expired. The timeout period elapsed prior to completion of the operation or the server is not responding.【转】

    今天碰到了一个查询异常问题,上网查了一下,感谢原创和译者 如果你使用的数据库连接类是 the Data Access Application Blocks "SqlHelper" ...

  2. Juno 版 Keystone 主配置文件 keystone.conf 详解

    本文全面解读Icehouse发行版keystone的配置文件keystone.conf,由于从keystone提供的服务或依赖的基础设施角度入手,因此[DEFAULT]部分可能被拆分到很多子块中. 关 ...

  3. MySQL库和表的管理

    MySQL数据库服务配置好后,系统会有4个默认的数据库. information_schema:虚拟对象,其对象都保存在内存中performance_schema:服务器性能指标库mysql:记录用户 ...

  4. 查看CentOS系统运行了多久使用uptime命令

    对于一些人来说系统运行了多久是无关紧要的,但是对于服务器管理员来说,这是相当重要的信息. 服务器在运行重要应用的时候,必须尽量保证长时间的稳定运行,有时候甚至要求零宕机. 那么我们怎么才能知道服务器运 ...

  5. 转 linux 权限

    发布系统架构图简化如下: 管理员通过Jenkins调用“发布程序(代号varian,以下简称varian)”,发布程序会进行一系列的初始化操作,完成后生成Docker镜像上传到Docker仓库,容器集 ...

  6. SQL2000系统表、存储过程、函数的功能介绍及应用

    转自:http://blog.csdn.net/zlp321002/article/details/480925 ----系统表------------------------------------ ...

  7. Spring Boot项目使用Eclipse进行断点调试Debug

    1.在命令行下定位到项目根目录,启动Spring Boot项目,命令如下: java -Xdebug -Xrunjdwp:server=y,transport=dt_socket,address=80 ...

  8. 2. 自动化运维系列之Cobbler给Openstack节点安装操作系统。

    preface 我们在一篇博文知道了如何搭建Cobbler,那么下面就通过Cobbler来安抓Openstack所有节点吧. 服务器配置信息如下: 主机名 IP 角色 Cobbler.node.com ...

  9. 将Unity导出的Eclipse工程转换为AndroidStudio工程

    步骤:1)将unity项目导出到文件夹: 转换到安卓平台,这里只勾选google android project.然后导出到自己新建的文件夹. 2)打开导出的文件夹,看到如下内容.这是unity5.x ...

  10. 基于thinkphp的API日志

    1.thinkphp日志 thinkphp的日志处理工作是由系统自动进行的 在开启日志记录的情况下,会记录下允许的日志级别的所有日志信息 系统的日志记录由核心的Think\Log类及其驱动完成,提供了 ...