因为对CORBA分析的需要,这里写一个简单的CORBA例子。从JDK1.2开始,JDK中集成了ORB的实现,本例子使用了JDK1.7,对于JDK1.2+应该都没有问题。这个例子实现一个简单的加减乘除的功能的计算器,客户端将参数和请求的方法名传送到服务端,服务端处理这个请求并将结果返回给客户端。

我们知道不同编程语言中的类型的表达,内存模型是不一样的,为此CORBA发明了一套中间描述语言IDL,不同语言平台的ORB实现负责将IDL中的类型映射到本地类型中。因此IDL是我们编写CORBA程序的出发点,首先,我们用IDL来描述我们的接口/对象:

 module com{
module bes{
module corba{
module test{
interface Calc{
void add(in long a,in long b,out long c);
void sub(in long a,in long b,out long c);
void multi(in long a,in long b,out long c);
void div(in long a,in long b,out long c);
};
};
};
};
};

当然接口Calc中的方法的返回值不一定为void,这里将返回值放到了out类型的参数c中,方法可以带有多个out类型的参数。然后我们用idlj工具(jdk自带)将Calc.idl转换为对应java的描述,并生成Stub和POA等类:

idlj给我们生成很多文件,首先我们来看一下UML图:

上面的图不涉及工具类CalcHelper和CalcHolder,这两个类的作用在后而阐述。

package com.bes.corba.test;

/**
* com/bes/corba/test/_CalcStub.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ public class _CalcStub extends org.omg.CORBA.portable.ObjectImpl implements com.bes.corba.test.Calc
{ public void add (int a, int b, org.omg.CORBA.IntHolder c)
{
org.omg.CORBA.portable.InputStream $in = null;
try {
org.omg.CORBA.portable.OutputStream $out = _request ("add", true);
$out.write_long (a);
$out.write_long (b);
$in = _invoke ($out);
c.value = $in.read_long ();
return;
} catch (org.omg.CORBA.portable.ApplicationException $ex) {
$in = $ex.getInputStream ();
String _id = $ex.getId ();
throw new org.omg.CORBA.MARSHAL (_id);
} catch (org.omg.CORBA.portable.RemarshalException $rm) {
add (a, b, c );
} finally {
_releaseReply ($in);
}
} // add public void sub (int a, int b, org.omg.CORBA.IntHolder c)
{
org.omg.CORBA.portable.InputStream $in = null;
try {
org.omg.CORBA.portable.OutputStream $out = _request ("sub", true);
$out.write_long (a);
$out.write_long (b);
$in = _invoke ($out);
c.value = $in.read_long ();
return;
} catch (org.omg.CORBA.portable.ApplicationException $ex) {
$in = $ex.getInputStream ();
String _id = $ex.getId ();
throw new org.omg.CORBA.MARSHAL (_id);
} catch (org.omg.CORBA.portable.RemarshalException $rm) {
sub (a, b, c );
} finally {
_releaseReply ($in);
}
} // sub public void multi (int a, int b, org.omg.CORBA.IntHolder c)
{
org.omg.CORBA.portable.InputStream $in = null;
try {
org.omg.CORBA.portable.OutputStream $out = _request ("multi", true);
$out.write_long (a);
$out.write_long (b);
$in = _invoke ($out);
c.value = $in.read_long ();
return;
} catch (org.omg.CORBA.portable.ApplicationException $ex) {
$in = $ex.getInputStream ();
String _id = $ex.getId ();
throw new org.omg.CORBA.MARSHAL (_id);
} catch (org.omg.CORBA.portable.RemarshalException $rm) {
multi (a, b, c );
} finally {
_releaseReply ($in);
}
} // multi public void div (int a, int b, org.omg.CORBA.IntHolder c)
{
org.omg.CORBA.portable.InputStream $in = null;
try {
org.omg.CORBA.portable.OutputStream $out = _request ("div", true);
$out.write_long (a);
$out.write_long (b);
$in = _invoke ($out);
c.value = $in.read_long ();
return;
} catch (org.omg.CORBA.portable.ApplicationException $ex) {
$in = $ex.getInputStream ();
String _id = $ex.getId ();
throw new org.omg.CORBA.MARSHAL (_id);
} catch (org.omg.CORBA.portable.RemarshalException $rm) {
div (a, b, c );
} finally {
_releaseReply ($in);
}
} // div // Type-specific CORBA::Object operations
private static String[] __ids = {
"IDL:com/bes/corba/test/Calc:1.0"}; public String[] _ids ()
{
return (String[])__ids.clone ();
} private void readObject (java.io.ObjectInputStream s) throws java.io.IOException
{
String str = s.readUTF ();
String[] args = null;
java.util.Properties props = null;
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init (args, props);
try {
org.omg.CORBA.Object obj = orb.string_to_object (str);
org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl) obj)._get_delegate ();
_set_delegate (delegate);
} finally {
orb.destroy() ;
}
} private void writeObject (java.io.ObjectOutputStream s) throws java.io.IOException
{
String[] args = null;
java.util.Properties props = null;
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init (args, props);
try {
String str = orb.object_to_string (this);
s.writeUTF (str);
} finally {
orb.destroy() ;
}
}
} // class _CalcStub

_CalcStub是存根类,有过远程调用编程经验的读者应该对这个词比较熟悉,它是远程对象在本地的一个代理(Proxy)。从业务划分角度来说,CORBA这些底层的东西不应该太多地污染到我们的应用,比如这个_CalcStub是我们不希望在业务代码中出现的,我们只需要看到我们需要的Calc。客户端从ORB中拿到的Calc接口实现其实是一个_CalcStub,客户程序对Calc接口中的方法进行调用时,_CalcStub将相应方法的调用转发到服务端,然后将服务器的响应返回给客户端,从而成功从欺骗客户端程序。

_CalcStub继承了ObjectImpl类,这使得_CalcStub能够关联到ORB环境中,从而完成远程调用。

idlj工具并没有直接在Calc.java中定义idlj方法的java语言描述,而是在CalcOperation.java中。Calc接口继承了IDLEntity,org.omg.CORBA.Object和CalcOperation三个接口:

IDLEntity是一个标记接口,表明Calc接口是一种IDL接口,这个与org.omg.CORBA.Object有点相类似,但实现了org.omg.CORBA.Object接口的对象不一定是IDL描述的,因此这里单独把IDLEntity拎出来。

org.omg.CORBA.Object接口定义了一些CORBA相关的方法,因为客户端所使用的是Calc,参数传递到ORB中语义上也是 Calc类型,当然我们不能将非IDL的对象传递到ORB中,ORB无法完成那样子的操作;Calc接口继承 org.omg.CORBA.Object(ORB层面使用的是org.omg.CORBA.Object),那就意味了通过编译器来保证类型安全(避免 强制转换,ClassCastException之类的异常)。

package com.bes.corba.test;

/**
* com/bes/corba/test/CalcOperations.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ public interface CalcOperations
{
void add (int a, int b, org.omg.CORBA.IntHolder c);
void sub (int a, int b, org.omg.CORBA.IntHolder c);
void multi (int a, int b, org.omg.CORBA.IntHolder c);
void div (int a, int b, org.omg.CORBA.IntHolder c);
} // interface CalcOperations

CalcOperation是相对应的IDL映射。

package com.bes.corba.test;

/**
* com/bes/corba/test/CalcPOA.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ public abstract class CalcPOA extends org.omg.PortableServer.Servant
implements com.bes.corba.test.CalcOperations, org.omg.CORBA.portable.InvokeHandler
{ // Constructors private static java.util.Hashtable _methods = new java.util.Hashtable ();
static
{
_methods.put ("add", new java.lang.Integer (0));
_methods.put ("sub", new java.lang.Integer (1));
_methods.put ("multi", new java.lang.Integer (2));
_methods.put ("div", new java.lang.Integer (3));
} public org.omg.CORBA.portable.OutputStream _invoke (String $method,
org.omg.CORBA.portable.InputStream in,
org.omg.CORBA.portable.ResponseHandler $rh)
{
org.omg.CORBA.portable.OutputStream out = null;
java.lang.Integer __method = (java.lang.Integer)_methods.get ($method);
if (__method == null)
throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE); switch (__method.intValue ())
{
case 0: // com/bes/corba/test/Calc/add
{
int a = in.read_long ();
int b = in.read_long ();
org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
this.add (a, b, c);
out = $rh.createReply();
out.write_long (c.value);
break;
} case 1: // com/bes/corba/test/Calc/sub
{
int a = in.read_long ();
int b = in.read_long ();
org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
this.sub (a, b, c);
out = $rh.createReply();
out.write_long (c.value);
break;
} case 2: // com/bes/corba/test/Calc/multi
{
int a = in.read_long ();
int b = in.read_long ();
org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
this.multi (a, b, c);
out = $rh.createReply();
out.write_long (c.value);
break;
} case 3: // com/bes/corba/test/Calc/div
{
int a = in.read_long ();
int b = in.read_long ();
org.omg.CORBA.IntHolder c = new org.omg.CORBA.IntHolder ();
this.div (a, b, c);
out = $rh.createReply();
out.write_long (c.value);
break;
} default:
throw new org.omg.CORBA.BAD_OPERATION (0, org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
} return out;
} // _invoke // Type-specific CORBA::Object operations
private static String[] __ids = {
"IDL:com/bes/corba/test/Calc:1.0"}; public String[] _all_interfaces (org.omg.PortableServer.POA poa, byte[] objectId)
{
return (String[])__ids.clone ();
} public Calc _this()
{
return CalcHelper.narrow(
super._this_object());
} public Calc _this(org.omg.CORBA.ORB orb)
{
return CalcHelper.narrow(
super._this_object(orb));
} } // class CalcPOA

CalcPOA:它工作在服务端,POA是Portable Object Adapter的缩写,这里Adapter(适配器)的语义是指适配相应的编程语言中的对象(比如在java中那意思就是指 Java对象的适配器),适配器的作用有三点:

1:接受客户端发过来的调用请求,反序列化(Unmarshalling)参数,方法名等,然后将请求分发给对应的Servant。POA和Servant之间的关系如下图。

2:将对象引用(Object Reference)和相应的Servant起来(可以看到Servant._object_id方法),比如我们在EJB中的有状态会话Bean。

3:负责Servant的生命周期管理(如创建,钝化,销毁等),这里又让我联想到了EJB的生命周期。到这里这里我们可以清楚Home接口存在的理由。

好吧,我们要实现的例子确实很简单,在这个例子中,读者只需要了解到第一点即可(CORBA水很深,很容易死里面去的)。idlj为我们生成的POA中,集Servant,CalcOperation和InvocationHandler于一身,有越殂代疱的嫌疑,当然这并不影响程序的正常执行,当然如果服务端比较关注2,3两点的话,自己实现POA还是很有必要的,但这已经超出了本文的范围。

同样地ORB的东西不应该玷污到我们服务端的业务逻辑 ,Servant和InvocationHandler将POA关联到ORB中去。注意Servant和InvocationHandler是两接口是分开的,这一点还不是太清楚(也许是为了特性的划分吧)。

package com.bes.corba.test;

/**
* com/bes/corba/test/CalcHelper.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ abstract public class CalcHelper
{
private static String _id = "IDL:com/bes/corba/test/Calc:1.0"; public static void insert (org.omg.CORBA.Any a, com.bes.corba.test.Calc that)
{
org.omg.CORBA.portable.OutputStream out = a.create_output_stream ();
a.type (type ());
write (out, that);
a.read_value (out.create_input_stream (), type ());
} public static com.bes.corba.test.Calc extract (org.omg.CORBA.Any a)
{
return read (a.create_input_stream ());
} private static org.omg.CORBA.TypeCode __typeCode = null;
synchronized public static org.omg.CORBA.TypeCode type ()
{
if (__typeCode == null)
{
__typeCode = org.omg.CORBA.ORB.init ().create_interface_tc (com.bes.corba.test.CalcHelper.id (), "Calc");
}
return __typeCode;
} public static String id ()
{
return _id;
} public static com.bes.corba.test.Calc read (org.omg.CORBA.portable.InputStream istream)
{
return narrow (istream.read_Object (_CalcStub.class));
} public static void write (org.omg.CORBA.portable.OutputStream ostream, com.bes.corba.test.Calc value)
{
ostream.write_Object ((org.omg.CORBA.Object) value);
} public static com.bes.corba.test.Calc narrow (org.omg.CORBA.Object obj)
{
if (obj == null)
return null;
else if (obj instanceof com.bes.corba.test.Calc)
return (com.bes.corba.test.Calc)obj;
else if (!obj._is_a (id ()))
throw new org.omg.CORBA.BAD_PARAM ();
else
{
org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();
com.bes.corba.test._CalcStub stub = new com.bes.corba.test._CalcStub ();
stub._set_delegate(delegate);
return stub;
}
} public static com.bes.corba.test.Calc unchecked_narrow (org.omg.CORBA.Object obj)
{
if (obj == null)
return null;
else if (obj instanceof com.bes.corba.test.Calc)
return (com.bes.corba.test.Calc)obj;
else
{
org.omg.CORBA.portable.Delegate delegate = ((org.omg.CORBA.portable.ObjectImpl)obj)._get_delegate ();
com.bes.corba.test._CalcStub stub = new com.bes.corba.test._CalcStub ();
stub._set_delegate(delegate);
return stub;
}
} }

CalcHelper:从名字上可以看出,它是一个工具类,它的职责在有:

1、通过提供narrow方法和RepositoryId等信息来实现类型安全。

2、与Any类型之间的进行转换。

3、从InputStream中读出Calc(对象引用),将Calc写出到OutputStream中。

我们的例子中将只用到第一点。

package com.bes.corba.test;

/**
* com/bes/corba/test/CalcHolder.java .
* 由IDL-to-Java 编译器 (可移植), 版本 "3.2"生成
* 从Hello.idl
* 2016年2月15日 星期一 下午09时08分34秒 CST
*/ public final class CalcHolder implements org.omg.CORBA.portable.Streamable
{
public com.bes.corba.test.Calc value = null; public CalcHolder ()
{
} public CalcHolder (com.bes.corba.test.Calc initialValue)
{
value = initialValue;
} public void _read (org.omg.CORBA.portable.InputStream i)
{
value = com.bes.corba.test.CalcHelper.read (i);
} public void _write (org.omg.CORBA.portable.OutputStream o)
{
com.bes.corba.test.CalcHelper.write (o, value);
} public org.omg.CORBA.TypeCode _type ()
{
return com.bes.corba.test.CalcHelper.type ();
} }

CalcHolder:这个类在Calc被作为out或者inout类型的参数传递时候被使用,如果我们在另一个IDL方法中使用Calc作为参数,那么生成的代码将会是这样子的:

void test(int a, int b, CalcHolder calc)

CalcHolder负责从InputStream或者OuputStream分别读出和写入Calc,从CalcHolder生成的代码中我们可以看出,CalcHolder的_read和_write方法将相应的操作委托给了CalcHelper。

哆嗦了这么多,是时候拿出我们的客户端和服务端了。

服务端代码:

CalculatorImpl是Servant的实现,它继承了CalcPOA类。

package com.bes.corba.impl;

import org.omg.CORBA.IntHolder;

import com.bes.corba.test.CalcPOA;

public class CalculatorImpl extends CalcPOA{

    @Override
public void add(int a, int b, IntHolder c) {
c.value=a+b;
} @Override
public void sub(int a, int b, IntHolder c) {
c.value=a-b;
} @Override
public void multi(int a, int b, IntHolder c) {
c.value=a*b;
} @Override
public void div(int a, int b, IntHolder c) {
c.value=a/b;
}
}

Server类:

package com.bes.corba.test;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper; import com.bes.corba.test.Calc;
import com.bes.corba.test.CalcHelper;
import com.bes.corba.impl.CalculatorImpl; public class Server {
public static void main(String[] args) throws Exception{
/*
* ORB 初始化。
*/
ORB orb=ORB.init(args,null); /*
* 获取根POA并初始化。
*/
POA rootPoa=POAHelper.narrow(orb.resolve_initial_references("RootPOA") );
rootPoa.the_POAManager().activate(); /*
* 构建一个CalculatorImpl。
*/
CalculatorImpl calculatorImpl=new CalculatorImpl(); /*
* 将Servant注册到RootPOA中,建立Servant到Object Reference的相互映射,
* 注意这里具体行为跟RootPOA的POA Policy有关。
*/
org.omg.CORBA.Object ref=rootPoa.servant_to_reference(calculatorImpl);
Calc iref=CalcHelper.narrow(ref); /*
* 获取命名服务。
*/
org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
NamingContextExt ncRef= NamingContextExtHelper.narrow(objRef); /*
* 将对象引用以相应的名字发布到命名服务中。
*/
String name="Calc";
NameComponent path[] = ncRef.to_name(name);
ncRef.rebind(path,iref); System.out.println("Calculator server ready..."); /*
* 阻塞直到ORB关闭。
*/
orb.run();
}
}

客户端代码Client

package com.bes.corba.test;

import org.omg.CORBA.IntHolder;
import org.omg.CORBA.ORB;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper; import com.bes.corba.test.Calc;
import com.bes.corba.test.CalcHelper; public class Client {
public static void main(String[] args) throws Exception{
/*
* ORB 初始化。
*/
ORB orb=ORB.init(args,null); /*
* 获取命名服务。
*/
org.omg.CORBA.Object objRef=orb.resolve_initial_references("NameService");
NamingContextExt ncRef=NamingContextExtHelper.narrow(objRef); /*
* 从命名服务中查找相应的对象引用,并进行类型转型。
*/ String name="Calc";
Calc calc=CalcHelper.narrow(ncRef.resolve_str(name)); /*
* 调用对象的方法。
*/
IntHolder result=new IntHolder(); calc.add(1,2,result);
System.out.printf("1+2=%d\n",result.value);
}
}

启动命名服务,命名服务器不一定运行在对象服务器的进程中,尤其是在一个分布式的环境中,命名服务器与对象服务器通常是一对多的关系。

orbd -ORBInitialPort 1050 -ORBInitialHost localhost&

启动服务端:

java HelloServer -ORBInitialPort 1050 -ORBInitialHost localhost

ORBInitialPort和ORBInitialHost参数指定了命名服务器的主机名和端口号。

启动客户端:

java com.bes.corba.test.Client -ORBInitialPort 1050 -ORBInitialHost localhost

一个简单的CORBA例子的更多相关文章

  1. 轻松创建nodejs服务器(1):一个简单nodejs服务器例子

    这篇文章主要介绍了一个简单nodejs服务器例子,本文实现了一个简单的hello world例子,并展示如何运行这个服务器,需要的朋友可以参考下   我们先来实现一个简单的例子,hello world ...

  2. 使用Multiplayer Networking做一个简单的多人游戏例子-3/3(Unity3D开发之二十七)

    使用Multiplayer Networking做一个简单的多人游戏例子-1/3 使用Multiplayer Networking做一个简单的多人游戏例子-2/3 使用Multiplayer Netw ...

  3. 使用Multiplayer Networking做一个简单的多人游戏例子-2/3(Unity3D开发之二十六)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/51007512 ...

  4. 使用Multiplayer Networking做一个简单的多人游戏例子-1/3(Unity3D开发之二十五)

    猴子原创,欢迎转载.转载请注明: 转载自Cocos2Der-CSDN,谢谢! 原文地址: http://blog.csdn.net/cocos2der/article/details/51006463 ...

  5. 一个简单的cmake例子

    一个简单的cmake例子CMakeLists.txt,生成动态库文件,可以指定发布目录. 尚不支持: 1.交叉编译环境配置 2.添加依赖库   #在当前目录新建一个build目录,然后cd build ...

  6. 一个简单的springmvc例子 入门(1)

    一直是从事棋牌游戏,平常用的东西 大多数只是使用一些javase的一些 api对spring 这方面 用到的比较少,每次学了都忘,始终记不住.为了 更轻松学习springboot,从新学习了sprin ...

  7. 利用VisualStudio单元测试框架举一个简单的单元测试例子

    本随笔很简单,不涉及mock和stub对象,而是只给出一个简单的利用Visual Studio单元测试框架的最简单例子.如果需要深入理解Unit Test的原理与艺术,请参考<The art o ...

  8. 撸一个简单的MVVM例子

    我个人以为mvvm框架里面最重要的一点就是VM这部分,它要与Model层建立联系,将Model层转换成可以被View层识别的数据结构:其次也要同View建立联系,将数据及时更新到View层上,并且响应 ...

  9. 企业级任务调度框架Quartz(3) 一个简单的Quartz 例子

    1. 一个简单的Quartz 工程     本示例应用比起众所周知的 System.out.println("Hello world from Quartz") 来还是要有趣些.当 ...

随机推荐

  1. 深入理解JS闭包

    一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...

  2. 转:Ajax中的get和post两种请求方式的异同

    1. get是把参数数据队列加到提交表单的ACTION属性所指的URL中,值和表单内各个字段一一对应,在URL中可以看到.post是通过HTTP post机制,将表单内各个字段与其内容放置在HTML ...

  3. NPOI 操作Excel

    关于NPOI NPOI是POI项目的.NET版本,是由@Tony Qu(http://tonyqus.cnblogs.com/)等大侠基于POI开发的,可以从http://npoi.codeplex. ...

  4. 关于ADDED_TO_STAGE事件

    可视类初始化的时候,很多时候要用到stage属性,则要使用Event.ADDED_TO_STAGE事件,这个swf被其它的文件加载,如果直接在初始化函数内使用stage属性 .但是,文档类初始化函数内 ...

  5. 二分查找C++

    #include <iostream> using namespace std; //二分查找:每次都从中间位置寻找,如果找到了就返回,如果没找到, //则分两种情况: //(1)中间元素 ...

  6. Day2 summary

    感觉今天就对电脑大扫除了. 卸了一早上cygwin也没找到方法,只能先放着不管,真上linux时才说. 搜了搜linux视频教程很多,应该好开个头.但这个毕竟是优先级不够高的. 高的必须是论文啊,还有 ...

  7. 在线生成CSS样式和兼容的字体格式

    http://www.fontsquirrel.com/tools/webfont-generator 在线生成CSS样式和兼容的字体格式.

  8. 从数学角度看最大期望(EM)算法 II

    [转载请注明出处]http://www.cnblogs.com/mashiqi 2015/3/13 对于隐变量只有有限个取值(比如$N$个)的情况,我们可以将隐变量表示为${z_j} = [{z_{j ...

  9. Qt 自定义 滚动条 样式

    今天是时候把软件中的进度条给美化美化了,最初的想法就是仿照QQ. 先前的进度条是这样,默认的总是很难受欢迎的:美化之后的是这样,怎么样?稍微好看一点点了吧,最后告诉你实现这个简单的效果在Qt只需要加几 ...

  10. QString转换为char* (转)

    Qt下面,字符串都用QString,确实给开发者提供了方便,想想VC里面定义的各种变量类型,而且函数参数类型五花八门,经常需要今年新那个类型转换 Qt再使用第三方开源库时,由于库的类型基本上都是标准的 ...