MYSQL的简单封装,学习基于托管的C++开发

5 篇文章 0 订阅

MYSQL的简单封装,学习基于托管的C++开发

 

又折腾了半天,弄明白了一些事情,一言难尽。

1、  背景。

最近在写一点尝试性的代码,需要做一个简单的原型来验证我的思路是否正确。这个原型由数据库(mysql)和前台界面构成。

 

Mysql的表/测试数据已经建好。

前台界面我不小心用了C++,鬼使神差的选择了CLR界面。(昨天折腾了半天总算搞明白CLR的指针^,调通了VC调用mysql。中间还有解决字符集的问题。)

今天本来想用回C#算了。但是略有点不甘心,遂重新写一遍看看托管的C++代码有啥不一样,于是重写昨天的调用mysql的代码,尝试进行封装一下。

 

2、  遇到的问题。

传统的sql调用来说,一般是bind输出变量,执行sql语句,使用输出变量。

例如,我设想中的调用是这样的

String^ myStringResult1;
Int  myIntResult1;
Double myDoubleResult1;
 
Mysql_bind_var(1,StringType,myStringResult1);
Mysql_bind_var(2,IntType,&myIntResult1);
Mysql_bind_var(3,DoubleType,&myDoubleResult1);
Mysql_select(3,” SELECT ‘hello,this is string’,12,12.345 “);
//接下来myStringResult1就等于’hello,this is string’,
// myIntResult1就等于12,myDoubleResult1就等于12.345

 

恩,很自然的,封装一个mysql的操作类

public ref class tsql{
 ConnectDB(xxx)
 DisConnectDB(xxx)
 BindVar(xxx);
 Select(xxx)…
}


实现BindVar的时候,最简单的就是做个数组,把绑定的变量登记进去,然后再Select实现的时候,予以实例化和赋值。

 

非托管代码实现时,很简单

void *m_pArray[MAX_VARS];
 
void mysql_bind_var(int a,int b,void *pv)
{
         M_pArray[a]=pv;
}
 
 
void mysql_select(int a,char *s)
{
           *(int*)(m_pArray[0])=12;
           *(double*)(m_pArray[1])=12.34;
           Strcpy((char*)(m_pArray[2]),”helloxxx”);
}
 


哦也,就是bind的时候记录一个地址,select的时候直接把取得的结果放进去好了。

 

那么用托管代码能否实现呢?折腾了半天的结果是不能。

 

这个问题归纳起来,就是说托管的变量,能否在除堆栈传参以外的地方被实例化,也即不能有指向托管指针的指针。(托管变量可以由托管指针初始化,托管指针不能被记录再其生命周期之外)

即:

 

例1:正常gcnew实例化

void Test001()
{
	String^ foo;
 
	foo = gcnew String(“hello”);               // 正确!foo=“hello”,正常实例化
}


例2:在函数调用中被实例化

void Test002_sub(String ^% pString)
{
           pString =gcnew String(“hello”);
}
 
void Test002()
{
           String^foo;
           Test002_sub(foo);                               // 正确!foo=“hello”,在堆栈传参中被实例化。
}
 

例3:在除堆栈传参以外的地方被实例化。

void Test003_setPointer(String ^% pString,RecordVar^%pRecord)
{
//      在pRecord中记录下pString的地址
}
void Test003_InitPointer(RecordVar ^% pRecord)
{
           // pRecord的pString初始化并赋值。
}
void Test003()
{
           String^foo;
 
           Test003_setPointer(foo,record);              // 没有办法做一个record能够记录 foo地址
           Test003_InitPointer(record);                     // 然后直接用record来初始化foo
                                                                                      //满怀希望的认为foo此时可用
}
 

我尝试了各种方法,包括System::Object打包,interior_ptr等等,都不行。

后来忽然想明白了,就是因为这个不能保证安全,当然被托管代码所不容许。

 

3、  原因:

如果允许记录托管指针的指针,那么就意味着允许托管指针有别名。当托管指针失效的时候,托管指针的指针应当如何自处呢?

 

如果允许指向托管指针的指针,那么则该托管指针不应当是栈上对象,而应该是托管堆中的对象。不然的话,指向托管指针的指针指向的内容将是可能被迫是非法内容(当程序运行到该托管指针的作用域范围外时,该托管指针将失效,指向该指针的指针也要失效)。

 

这解释了为什么不能有指向托管指针的指针,也解释了为什么interior_ptr只能在堆栈上。

因为interior_ptr生命周期应当与其程序执行的作用域相等,为确保它的生命周期与其程序执行的作用域相等,它也不能被赋值给别的变量。

 

换一句话说,就是托管模式下,一个变量不能被可能不在其生命周期之外的地方所改变。(太拗口了,就算我一开始明白这个也会被绕进去)

 

4、  托管模式的代码:

出于安全考虑,禁止了托管指针的指针。

So,这样最简单的,就是不要让调用者的托管变量离开“调用者”的视野。

 

改成了这样的模式:

 

OutParamo1,o2,o3;
Int iResult1;
String^sResult1;
Double dResult1;
 
Sql_select(“select xx ,xx ,xx from xxx where xxx “, o1,o2,o3);
iResult1 = o1;
sResult1= o2;
dResult1 =o3;
 
sql_select(String^ sqlStmt,…array<System::Object^>^pOutParams)
{
           OutParam ^po;
           for(i=0;i<pOutParams->Length;i++)
{
                   po= (OutParam^)pOutParams[i];
                    if (i == 1)
                            po->result= gcnew String(“hello”);
                    if (i == 2)
                            po->result= gcnew int(12);
                    if (i == 3)
                            po->result= gcnew double(12.34);
           }
}
 
public ref class OutParam
{
public: OutParam(){};
           operator int(){
                    return (int)result;
           }
           operator System::String^(){
                    return (System::String^)result;
           }
           operator double(){
                    return (double)result;
           }
           System::Object ^result;           
}
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值