国产编程语言R++语法

  1. <span style=“font-size:18px;”>1. 1+2=3  
  2.   
  3. 很多语言的第一个程序都是hello world,但是它太难了。下面是R++的第一个例子。  
  4.   
  5. main  
  6. {  
  7.     a=1  
  8.     b=2  
  9.     puts a+b  
  10. }  
  11.   
  12.   
  13.   
  14.   
  15.   
  16. 2. C  
  17.   
  18. R++也支持C风格,上一个程序可以写成这样:  
  19.   
  20. void main()  
  21. {  
  22.     int a=1;  
  23.     int b=2;  
  24.     puts(a+b);  
  25. }  
  26.   
  27. 对比上一节的程序可以看到R++的一些特点:  
  28. *更彻底地类型推断,C++11需要使用关键字auto,R++可以省略auto  
  29. *函数定义和函数调用均可省略后面的小括号  
  30. *语句后面的分号也可以省略  
  31. *对于返回值为void的函数,可以省略void(标准C默认返回int)  
  32.   
  33.   
  34.   
  35.   
  36.   
  37.   
  38.   
  39.   
  40. 3. 面向对象  
  41.   
  42. 同时R++也支持Java(C#)风格,比如上一个程序可以写成这样:  
  43.   
  44. public class main  
  45. {  
  46.     public static void main()  
  47.     {  
  48.         int a=1;  
  49.         int b=2;  
  50.         puts(a+b);  
  51.     }  
  52. }  
  53.   
  54.   
  55.   
  56. 4. S表达式  
  57.   
  58. R++试图模仿Lisp,它可以很好的融合C++的中缀表达式和Lisp的S表达式:  
  59.   
  60. main  
  61. {  
  62.     int a  
  63.     int b  
  64.     [= a 1]  
  65.     [= b 2]  
  66.     [rf print [+ a b]]  
  67. }  
  68.   
  69. 其中  
  70. [rf print [+ a b]]  
  71. 等价于  
  72. [rf print (a+b)]  
  73. 等价于  
  74. rf.print([+ a b])  
  75. 等价于  
  76. rf.print(a+b)  
  77.   
  78. 注意S表达式不以逗号作为分隔符,  
  79. 所以适当的时候需要加上括号。  
  80.   
  81. 另一个S表达式的例子:  
  82. example/4.2.h  
  83.   
  84.   
  85.   
  86.   
  87.   
  88. 5. 省略括号  
  89.   
  90. 控制结构(if/for/while)后面的小括号是可以省略的,比如有一个递归求和的函数:  
  91.   
  92. int sum(int a)  
  93. {  
  94.     if a<=1  
  95.         return 1  
  96.     return sum(a-1)+a  
  97. }  
  98.   
  99. 那么  
  100. if a<=1  
  101. 等价于  
  102. if(a<=1)  
  103.   
  104. 如果if后面有括号,则括号不能省略  
  105. 即不要写成   
  106. if (a)<=1  
  107. 正确的写法是   
  108. if((a)<=1)  
  109.   
  110. 如果函数调用的右边没有小括号,  
  111. 则本行后面所有单词均作为函数的参数  
  112. 比如  
  113. puts(sum(9))  
  114. 等价于  
  115. puts sum(9)  
  116. 等价于  
  117. puts sum 9  
  118.   
  119. R++ v1.5表达式过长可以折行:  
  120. main  
  121. {  
  122.     if(1||  
  123.         2)  
  124.         puts “true”  
  125.   
  126.     func(1,  
  127.         2)  
  128.   
  129.     func(1,2  
  130.         )  
  131. }  
  132.   
  133. func(int a,  
  134.     int b)  
  135. {  
  136.     puts a+b  
  137. }  
  138.   
  139. 但是左圆括号不可折行。  
  140. 如果表达式过长又没有圆括号(比如return),请加上圆括号。  
  141.   
  142.   
  143.   
  144. 6. 常量当做对象  
  145.   
  146. 常量可以作为对象使用,这只是编译器提供的一个语法糖  
  147. 比如  
  148. 2.print  
  149. 会替换为  
  150. int(2).print  
  151.   
  152. “123”+“abc”  
  153. 会替换为  
  154. rstr(“123”)+rstr(“abc”)  
  155.   
  156.   
  157. 和C++一样,  
  158. 类名后面接小括号表示生成临时对象  
  159. rstr(“123”)  
  160. 会生成一个临时的rstr对象  
  161. int(2)  
  162. 会生成一个临时的int对象  
  163.   
  164.   
  165.   
  166. 7. 运算符DIY  
  167.   
  168. R++支持自定义新的运算符,也可以自定义运算符的优先级,请手动修改rinf/optr.txt这个文件。  
  169. 比如  
  170. %  
  171. 2  
  172. 这个%后面跟的数字就是求余运算符的优先级,修改这个数字即可。但不要调换里面运算符的顺序,因为有些运算符是编译器使用的。如果要增加新的运算符,请在文件末尾增加两行。(不能自定义>>运算符)  
  173.   
  174.   
  175. 借助于R++的运算符自定义功能可以实现一些很有趣的功能  
  176. 比如  
  177. D=(A∩B)∪(A∩C)  
  178. 这是数学书上的一个表达式,  
  179. 在R++里只要自定义∪和∩两个运算符然后设定好优先级,  
  180. 再写两个运算符函数  
  181.   
  182. friend vector<T> operator∪(vector<T>& a,vector<T>& b)  
  183. {  
  184.     …  
  185. }  
  186.   
  187. friend vector<T> operator∩(vector<T>& a,vector<T>& b)  
  188. {  
  189.     …  
  190. }  
  191.   
  192. 这段表达式就是可以运行的。  
  193. 看起来就像是你自己定义了一个专用于数学集合运算的新语言  
  194. 因此,R++这种灵活的语法很适合实现领域特定语言(DSL)  
  195.   
  196.   
  197. R++相同优先级的运算符都是从左往右运算的  
  198. C语言可以这样  
  199. **p=2  
  200. 但R++只能这样  
  201. *(*p)=2  
  202. 注意R++的乘法运算符和指针运算符都是*,优先级相同,均为2。  
  203. 而标准C乘法运算符优先级是2,指针运算符优先级是1,两者明显有区别。  
  204.   
  205.   
  206. R++的系统库也不支持连续赋值,  
  207. C语言里可以这样  
  208. a=b=2  
  209. R++只能这样  
  210. b=2  
  211. a=b  
  212. 如果一定要连续赋值,可以修改rsrc/int.h这个文件  
  213. 将operator=这个函数改成这样:  
  214.   
  215. int& operator=(int a)  
  216. {  
  217.     mov esi,this  
  218.     mov [esi],a  
  219.     return this  
  220. }  
  221.   
  222. 然后就可以这样  
  223. a=(b=2)  
  224. 注意括号是必须的  
  225.   
  226. 可以看到这样的话多返回了一个引用,影响效率  
  227.   
  228.   
  229. rsrc/int.h里面还添加一个反向赋值的运算符函数  
  230.   
  231. friend int& operator=>(int a,intthis)  
  232. {  
  233.     mov esi,this  
  234.     mov [esi],a  
  235.     return this  
  236. }  
  237.   
  238. 那么使用这个运算符就可以连续反向赋值  
  239. 2=>a=>b  
  240. 这样a和b都会赋值为2,其实从左往右赋值有时看起来更直观一些。  
  241.       
  242.   
  243. 打开rsrc/basic.h可以找到  
  244. define <- =  
  245. define <> !=  
  246.   
  247. 可见赋值运算符还有另一种写法  
  248. a <- 2  
  249. 其等价于  
  250. a = 2  
  251.   
  252. 而不等于运算符也有另一种写法  
  253. a != 2  
  254. 等价于  
  255. a <> 2  
  256. BASIC语言的<>好像看起来更直观一些  
  257.   
  258.   
  259. 标准C中减法运算符和负号运算符都是 –  
  260. 而R++中减法运算符是 –  负号运算符是 neg  
  261. 下面3句是等价的:  
  262. a=-2  
  263. a=(-2)  
  264. a=neg 2  
  265.   
  266. 对于加了括号的常量表达式,会在编译阶段进行求值  
  267.   
  268.   
  269.   
  270.   
  271. 8. 无缝内联汇编  
  272.   
  273. 在上一节的例子中可以看到,R++用this引用代替了C++的this指针,实际上R++内部引用和指针完全就是一个东西,只是外部访问的时候有些区别  
  274. C++是这样  
  275. this->print  
  276. R++则是  
  277. this.print  
  278.   
  279. 同时也可以看到R++支持无缝内联汇编  
  280. C内联汇编需要用关键字asm  
  281. asm mov esi,a  
  282. 或者  
  283. asm  
  284. {  
  285.     mov esi,a  
  286. }  
  287. R++可以省略关键字asm  
  288. mov esi,a  
  289.   
  290. a是一个局部变量,编译器会把它替换为  
  291. mov esi,[ebp+n]  
  292. 其中n是局部变量a的偏移  
  293.   
  294. R++的栈空间安排是这样的:  
  295. (地址从低地址到高地址)  
  296.   
  297. ebp  
  298. 局部变量1  
  299. 局部变量2  
  300. 局部变量n  
  301. 返回地址  
  302. 函数参数1  
  303. 函数参数2  
  304. 函数参数n  
  305. 返回值  
  306.   
  307.   
  308. 返回值由调用者析构,而函数参数和局部变量由被调用者析构,函数参数从右往左入栈。注意和stdcall以及cdecl都是有区别的,R++的返回值统一放在堆栈中传递,而不是通过eax  
  309.   
  310. 举个例子:  
  311.   
  312. friend int operator+(int a,int b)   
  313. {  
  314.     add a,b  
  315.     mov s_ret,a  
  316. }  
  317.   
  318. 编译器生成的汇编代码是这样  
  319. push ebp  
  320. mov ebp , esp  
  321. add [ ebp + 8 ] , [ ebp + 12 ]  
  322. mov [ ebp + 16 ] , [ ebp + 8 ]  
  323. pop ebp  
  324. reti 8  
  325. 其中有四条汇编语句是编译器生成的,另外两条是程序员写的  
  326.   
  327. 由于R++所有类和函数都是public,故友元的含义已经变了,friendstatic是同义语,它们都表示该函数不会自动生成this引用,也就是该函数无法访问到本类的数据成员  
  328.   
  329. 另外,从V1.5开始operator不再是R++的关键字,也就是说  
  330. friend int operator+(int a,int b)   
  331. 等价于  
  332. friend int +(int a,int b)   
  333.   
  334. 再看看如何调用int.+(int,int)这个函数  
  335. 表达式  
  336. 1+2  
  337. 会首先翻译为  
  338. int . + ( 1 , 2 )  
  339. 再翻译成汇编代码  
  340. sub esp , 4  
  341. push 2  
  342. push 1  
  343. call &int.+(int,int)  
  344. mov esi , esp  
  345. mov ebx , [ esi ]  
  346. add esp , 4  
  347. 结合上面的栈空间安排表可以很清楚的看到函数的调用过程  
  348.   
  349.   
  350. 所有的寄存器也可以当做一个int类型的变量使用  
  351. 比如想查看一下esp的值可以直接这样:  
  352. puts esp  
  353. 想计算ecx与edx的商可以这样:  
  354. puts ecx/edx  
  355. 判断寄存器是否为0:  
  356. if(eax)  
  357. {  
  358.     …  
  359. }  
  360. 但是不可以对寄存器赋值,下面这样是错误的  
  361. eax=1+2  
  362. 可以改为  
  363. 1+2  
  364. mov eax,ebx  
  365. 因为凡是返回int或者bool或者指针或者引用的表达式,  
  366. 编译器均会把这个返回值保存到ebx中。  
  367.   
  368. R++中boolint是一样的都是占用4个字节(C++的bool只占用一个字节)  
  369.   
  370.   
  371. R++的汇编指令与x86大部分是相同的,只是另外增加了一些虚拟指令(也可以理解为伪指令),但是这些虚拟指令很容易转换成标准的x86指令。完整的指令列表请参考inside/下的源码。  
  372.   
  373.   
  374.   
  375. 9. 伪代码  
  376.   
  377. 可以把R++当做伪代码写,下面就是《算法导论》开篇的插入排序代码(由于R++数组从下标0开始而算法导论从1开始,故稍微修改了下)  
  378.   
  379. define ← =  
  380.   
  381. insertion_sort(rstr& a)  
  382. {  
  383.     for j ← 1 to a.count-1  
  384.     {  
  385.         key ← a[j]  
  386.         i ← j-1  
  387.         while i>=0 && a.get(i)>key  
  388.         {  
  389.             a[i+1] ← a[i]  
  390.             i ← i-1  
  391.         }  
  392.         a[i+1] ← key  
  393.     }  
  394. }  
  395.   
  396. main  
  397. {  
  398.     rstr a=“cab132”  
  399.     putsl a  
  400.     insertion_sort a  
  401.     putsl a  
  402. }  
  403.   
  404. 其中for…to…也是一种语法糖  
  405. for i=1 to 10  
  406. 等价于  
  407. for i=1;i<=10;i++  
  408.   
  409. 对于方法(函数)参数为空的情况,可以省略后面的括号  
  410. 即  
  411. a.count  
  412. 等价于  
  413. a.count()  
  414.   
  415.   
  416.   
  417. 10. 模板函数  
  418.   
  419. R++的函数模板可以替换任何单词,跟宏差不多,  
  420. 比如有一个递归求和的函数(参考第5节)和一个递归求阶乘的函数  
  421.   
  422. int sum(int a)  
  423. {  
  424.     if a<=1  
  425.         return 1  
  426.     return sum(a-1)+a  
  427. }  
  428.   
  429. int pro(int a)  
  430. {  
  431.     if a<=1  
  432.         return 1  
  433.     return pro(a-1)*a  
  434. }  
  435.   
  436. 发现这两个函数极其相似,那么可以这样定义一个模板函数  
  437.   
  438. int func<T>(int a)  
  439. {  
  440.     if a<=1  
  441.         return 1  
  442.     return func<T>(a-1) T a  
  443. }  
  444.   
  445. 像下面这样调用就可以了  
  446.   
  447. main  
  448. {  
  449.     puts func<+>(10)  
  450.     puts func<*>(10)  
  451.       
  452.     puts func<+> 10  
  453.     puts func<*> 10  
  454. }  
  455.   
  456. R++ v1.5的模板函数十分强大,不仅支持模板函数作为类的成员,还支持模板动态生成:  
  457. main  
  458. {  
  459.     int* p=1p  
  460.     p.to<char*>.printl  
  461.     putsl typeof(p.to<char*>)  
  462.   
  463.     A a  
  464.     puts a.func<2>  
  465.     puts a.func<5>  
  466. }  
  467.   
  468. class A  
  469. {  
  470.     int m_a=2  
  471.   
  472.     int func<T>  
  473.     {  
  474.         return m_a+T  
  475.     }  
  476. }  
  477.   
  478. 可以看到模板函数和普通使用方法完全相同,  
  479. 也不需要关键字template,看起来更简洁一些。  
  480. 但是R++的模板函数暂不支持类型推测,  
  481. 也就是说调用模板函数时后面的尖括号是必须的。  
  482.   
  483.   
  484.   
  485. 11. 宏  
  486.   
  487. R++的define是在预处理阶段进行替换,功能比较弱,一般不需要使用。  
  488.   
  489. R++另有一个更好用的宏mac,  
  490. mac宏属于类的成员,而不是作用于全局,这样可以更好地封装。  
  491. 比如  
  492. mac fadd(a,b) a+b  
  493. 等价于C语言的  
  494. #define fadd(a,b) ((a)+(b))  
  495.   
  496. 对于mac宏R++会自动加上一些小括号避免优先级问题  
  497. c=fadd(1,2)*fadd(1,2)  
  498. puts c  
  499. 将输出正确的值9  
  500.   
  501. 另外R++还支持另一种不加括号的宏  
  502. 比如  
  503.   
  504. mac fadd2(a,b)  
  505. {  
  506.     a+b  
  507. }  
  508.   
  509. 这时用  
  510. c=fadd2(1,2)*fadd2(1,2)  
  511. puts c  
  512. 得到的是5,显然不是我们想要的结果  
  513.   
  514. 但是这种宏另有不错的用法  
  515. 请看下面的例子:  
  516.   
  517.   
  518. main  
  519. {  
  520.     pro=1  
  521.     for i=2;i<=10;i++  
  522.         pro+=i  
  523.     puts pro  
  524.       
  525.     pro=1  
  526.     for i=2;i<=10;i++  
  527.         pro*=i  
  528.     puts pro  
  529. }  
  530.   
  531. 这是一个循环求和与循环求阶乘的程序  
  532. 可以看到两段代码明显有相似之处,  
  533. 那么用mac定义一个宏  
  534.   
  535. mac fpro(T)  
  536. {  
  537.     pro=1  
  538.     for i=2;i<=10;i++  
  539.         pro T i  
  540.     puts pro  
  541. }  
  542.   
  543. 最后可以这样调用  
  544.   
  545. main  
  546. {  
  547.     fpro(+=)  
  548.     fpro(*=)  
  549. }  
  550.   
  551. 可以看到这与模板函数极其相似,  
  552. 与C语言的宏相比不需要写一堆的折行符号\  
  553. 实际上R++也没有折行符号  
  554.   
  555.   
  556. 另外,R++还支持一种拆分宏  
  557. 比如  
  558. #putsl(1,’abc’,98)  
  559. 宏展开后是这样:  
  560. putsl(1)  
  561. putsl(‘abc’)  
  562. putsl(98)  
  563.   
  564. 那么连续插入vector或者set就很方便了  
  565. vector<int> v  
  566. #v.push(1,99,2)  
  567.   
  568. 拆分宏的另一种用法请参考example/11.3.h  
  569.   
  570.   
  571.   
  572.   
  573.   
  574. 12. 函数指针  
  575.   
  576. R++使用一个比C语言更简单的函数指针语法  
  577. 比如有一个这样的函数:  
  578.   
  579. int fadd(int a,int b)  
  580. {  
  581.     return a+b;  
  582. }  
  583.   
  584. C语言通常是这样  
  585. typedef int (*FADD)(int,int);  
  586. FADD p=fadd;  
  587. p(1,2);  
  588. 或者  
  589. int (*p)(int,int)=fadd;  
  590. p(1,2);  
  591.   
  592. 可以看到C语言必须要指定函数指针的类型  
  593. 但R++可以直接这样调用  
  594. int[&fadd,1,2]  
  595. 或者  
  596. p=&fadd  
  597. int[p,1,2]  
  598.   
  599. 中括号左边表示返回值的类型,(即使返回值为空也必须写void)  
  600. 中括号里面的第一个参数是函数的地址  
  601. 而后面的参数编译器会自动推断出类型  
  602.   
  603. &fadd表示获取到函数的地址,这是一个静态地址,  
  604. 注意与标准C的区别,前面的取地址运算符是必须的  
  605.   
  606. 由于R++的函数指针只有一种类型,即  
  607. void*  
  608. 那么  
  609. &fadd  
  610. 得到一个类型为 void* 的指针常量  
  611.   
  612. 如果有多个名字都是fadd的函数(重载),  
  613. 那就必须指定参数的类型:  
  614. &fadd(int,int)  
  615.   
  616. 如果类A需要获取到类B的某一函数的地址,  
  617. 那就必须指定类名  
  618. &B.fadd(int,int)  
  619. 或者  
  620. &B::fadd(int,int)  
  621. 因为R++中作用域运算符和成员运算符是等价的  
  622.   
  623.   
  624. 再看一个函数  
  625.   
  626. func(int& a)  
  627. {  
  628.     …  
  629. }  
  630.   
  631. 千万不要这样调用  
  632. a=2  
  633. void[&func,a]  
  634. 因为func的第一个参数a的类型是引用  
  635. 正确的做法是:  
  636. a=2  
  637. void[&func,&a]  
  638.   
  639. 如果有另外一个函数  
  640.   
  641. func2(int* a)  
  642. {  
  643.     …  
  644. }  
  645.   
  646. 也可以这样调用:  
  647. a=2  
  648. void[&func2,&a]  
  649.   
  650.   
  651.   
  652. 13. 动态调用函数  
  653.   
  654. 在运行的过程中决定调用哪一个函数(跟虚函数有点像)  
  655. 这个特性在一些动态语言(python、ruby等)中十分常见。  
  656.   
  657. 还是拿上一节的函数举例:  
  658.   
  659. int fadd(int a,int b)  
  660. {  
  661.     return a+b  
  662. }  
  663.   
  664. 那么可以根据字符串找到这个函数的地址  
  665. p=findf(“fadd”)  
  666. 然后调用  
  667. puts int[p,1,2]  
  668.   
  669. 或者合并上面两句:  
  670. puts int[findf(“fadd”),1,2]  
  671.   
  672. 如果有函数重载的情况,就必须明确参数的类型  
  673. p=findf(“fadd(int,int)”)  
  674. 或者  
  675. p=findf(“main.fadd(int,int)”)  
  676.   
  677. 注意这种用法和上一节的函数指针有很大的区别,  
  678. 上一节的函数地址是在编译时确定的,  
  679. 而本节的函数地址是通过在运行时查找字符串获取到的。  
  680.   
  681.   
  682. 14. 元函数  
  683.   
  684. 就是用一个程序去生成另一个程序,  
  685. 这个特性在lisp和javascript中十分常见。  
  686.   
  687. 例如:  
  688. void[meta(‘void lambda(){puts 123}’)]  
  689. 等价于  
  690. void[meta(‘(){puts 123}’)]  
  691. 可以看到元函数的定义方法和普通函数没有区别,  
  692.   
  693. 一般支持元编程的语言都可以用一行代码实现计算器,  
  694. R++的版本是这样:  
  695. void[meta(‘(){putsl ‘+getsl+‘}’)]  
  696. 或者可以更简单:  
  697. self(‘putsl ‘+getsl)  
  698.   
  699. R++的self拥有其他语言不具备的能力:  
  700. main  
  701. {  
  702.     int a=0  
  703.     self(‘a=3’)  
  704.     puts a  
  705. }  
  706.   
  707. 即self可以和自身进行交互,直接访问函数的局部变量或者参数。  
  708. 访问类成员变量也很容易:  
  709. class A  
  710. {  
  711.     int m_a=2  
  712.       
  713.     func  
  714.     {  
  715.         self(‘puts this.m_a’)  
  716.     }  
  717. }  
  718.   
  719. 另外,R++的元函数是线程安全的。  
  720.   
  721.   
  722.   
  723. 15. 动态类型  
  724.   
  725. R++既支持静态类型也支持动态类型  
  726. 比如可以这样写  
  727.   
  728. func(a,b)  
  729. {  
  730.     puts a+b  
  731. }  
  732.   
  733. 然后这样调用  
  734.   
  735. main  
  736. {  
  737.     var a=1  
  738.     var b=2  
  739.     func a,b  
  740.   
  741.     a=‘hello’  
  742.     b=’34’  
  743.     func a,b  
  744. }  
  745.   
  746. 输出是  
  747. 3 hello34  
  748. 可以看到var中保存了变量的类型,  
  749. 那么就可以在运行过程中根据类型调用合适的函数  
  750.   
  751. 参考rsrc/rt.h可以发现目前R++的动态类型还十分简陋。  
  752. 不过配合typeof关键字和动态调用函数可以实现完整的动态类型。  
  753. typeof用法请参考:  
  754. example/15.2.h  
  755.   
  756.   
  757.   
  758.   
  759.   
  760.   
  761.   
  762.   
  763. 16. 反射  
  764.   
  765. R++拥有完整的反射和自省机制,解释器和R++代码几乎可以融为一体:  
  766.   
  767. import ‘rpp.h’  
  768.   
  769. main  
  770. {  
  771.     tasm* p=&main  
  772.     p->ptfi->name.printl  
  773.     p->ptfi->ptci->name.printl  
  774. }  
  775.   
  776. 上面的代码可以打印出自身的所属的函数和类,  
  777. 而打印自身的汇编代码以及表达式语句也很容易:  
  778.   
  779. import ‘rpp.h’  
  780.   
  781. main  
  782. {  
  783.     r_print_asm(‘main’)  
  784.     putsl  
  785.     r_print_sent(‘main’)  
  786. }  
  787.   
  788. 遍历所有类请参考example/16.3.h以及rsrc/rpp.h  
  789.   
  790.   
  791.   
  792.   
  793.   
  794.   
  795.   
  796. 18. 多重继承  
  797.   
  798. R++支持多重继承,不过R++的继承方式很特别,简单而有效  
  799. 举例说明:  
  800.   
  801. class A  
  802. {  
  803.     int m_a  
  804.   
  805.     fa  
  806.     {  
  807.         m_a=1  
  808.     }  
  809. }  
  810.   
  811. class B:A  
  812. {  
  813.     fb  
  814.     {  
  815.         m_a=2  
  816.     }  
  817. }  
  818.   
  819. 然后这样使用:  
  820. B b  
  821. b.fa  
  822. puts b.m_a  
  823. b.fb  
  824. puts b.m_a  
  825.   
  826. 可以看到B类继承了A类的数据成员m_a和函数成员fa  
  827.   
  828. 对于B类继承自A类,R++只是简单地拷贝代码,  
  829. 因此,R++的继承对程序员是透明的:  
  830.   
  831. class B  
  832. {  
  833.     int m_a  
  834.   
  835.     fa  
  836.     {  
  837.         m_a=1  
  838.     }  
  839.   
  840.     fb  
  841.     {  
  842.         m_a=2  
  843.     }  
  844. }  
  845.   
  846. 就像上面这样,直接把A类的所有代码拷贝到B类的头部。这也是R++的哲学:熟练地运用“Ctrl+C”和“Ctrl+V”可以省下人生80%的时间。  
  847.   
  848. 显然,R++的子类构造时不会自动调用父类的构造函数。  
  849. 且不允许父类和子类存在同名且参数类型和个数都相同的函数。  
  850.   
  851.   
  852. R++目前支持3种继承:  
  853. *模板继承模板 A<T>:B<T>  
  854. *模板继承非模板 A<T>:C,D  
  855. *非模板继承非模板 E:C  
  856.   
  857. 暂不支持继承模板实例 C:A<int>  
  858.   
  859.   
  860. 多重继承的例子请参考  
  861. example/18.2.h  
  862.   
  863.   
  864.   
  865.   
  866. 19. 可变参数  
  867.   
  868. R++使用中括号进行可变参数调用,  
  869. 求两个数的和:  
  870. sum[1,2]  
  871. 求三个数的和:  
  872. sum[1,2,3]  
  873.   
  874. 实际上R++会自动把参数的个数作为参数传递过去,即  
  875. sum[1,2]  
  876. 等价于  
  877. int[&sum,2,1,2]  
  878.   
  879. 目前R++的可变参数用起来虽然简单,写起来却比较痛苦:  
  880. int sum(int count)  
  881. {  
  882.     //sub esp,sizeof(s_local)  
  883.     //push ebp  
  884.     //mov ebp,esp  
  885.   
  886.     int* p=&count+1  
  887.     int ret=0  
  888.     for(i=0;i<count;i++)  
  889.     {  
  890.         ret+=*p  
  891.         p++  
  892.     }  
  893.     *p=ret  
  894.   
  895.     mov ecx,4  
  896.     imul ecx,count  
  897.     add ecx,4  
  898.     pop ebp  
  899.     add esp,sizeof(s_local)  
  900.     reti ecx  
  901.     //这里有编译器自动增加的语句  
  902. }  
  903.   
  904. 类成员函数使用可变参数的例子是:  
  905. example/19.2.h  
  906.   
  907. 20. 默认参数  
  908.   
  909. 举例说明:  
  910. int func(int a,int b=a)  
  911. {  
  912.     return a+b;  
  913. }  
  914.   
  915. main  
  916. {  
  917.     puts func(2)  
  918.     puts func(1,2)  
  919. }  
  920.   
  921. 有必要解释一下它的工作原理,  
  922. 对于上面的func函数  
  923. 编译器将自动生成两个函数:  
  924.   
  925. int func(int a)  
  926. {  
  927.     int b=a;  
  928.     return a+b;  
  929. }  
  930.   
  931. int func(int a,int b)  
  932. {  
  933.     return a+b;  
  934. }  
  935.   
  936. 显然,R++的默认参数比C++更灵活,  
  937. 因为后面的参数不仅可以访问到前面的参数,  
  938. 还可以访问到类的成员变量:  
  939. class A  
  940. {  
  941.     int m_a  
  942.       
  943.     func(int a,int b=m_a)  
  944.     {  
  945.         …  
  946.     }  
  947. }  
  948. 21. 多线程  
  949.   
  950. C++通常是这样封装线程的:  
  951.   
  952. class A  
  953. {  
  954.     int m_a;  
  955.   
  956.     static voidthread(void* param)  
  957.     {  
  958.         A* pthis=(A*)param;  
  959.         pthis->m_a=2;  
  960.         …  
  961.     }  
  962. }  
  963.   
  964. R++则是这样:  
  965.   
  966. class A  
  967. {  
  968.     int m_a  
  969.   
  970.     static void thread(A& this)  
  971.     {  
  972.         this.m_a=2  
  973.         …  
  974.     }  
  975. }  
  976.   
  977. 或者可以更简单地:  
  978.   
  979. class A  
  980. {  
  981.     int m_a  
  982.   
  983.     thread  
  984.     {  
  985.         m_a=2  
  986.         …  
  987.     }  
  988. }  
  989.   
  990. 因为不加static编译器会自动生成一个this引用。  
  991.   
  992. 创建线程直接调用函数即可:  
  993. A a  
  994. p=rf.create_thr(&A.thread,&a)  
  995.   
  996. 如果需要等待线程退出则:  
  997. rf.wait_thr(p)  
  998.   
  999. 如果不想把线程封装进类可以直接这样:  
  1000. rf.create_thr(&thread)  
  1001.   
  1002.   
  1003.   
  1004. 22. 本地调用  
  1005.   
  1006. 由于R++的所有数据类型和C/C++二进制兼容,  
  1007. 因此R++调用外部函数十分方便:  
  1008. stdcall(“MessageBoxA”,0,“abc”,“123”,0)  
  1009. 注意R++的字符串常量统一使用UTF8,  
  1010. 调用UTF16版本的API需要转换编码:  
  1011. stdcall(“MessageBoxW”,0,utf16c(“abc”),utf16c(“123”),0)  
  1012.   
  1013. 调用外部的cdecl函数:  
  1014. cdecl(“strlen”,“abc”)  
  1015.   
  1016. 如果DLL没有加载到当前的进程空间,请先调用LoadLibraryA(W)。  
  1017.   
  1018.   
  1019.   
  1020. 23. 类型转换  
  1021.   
  1022. R++是强类型语言,没有强制转换,可以通过函数转换类型  
  1023. 比如  
  1024. 2.touint  
  1025. 或者  
  1026. 2.to<uint>  
  1027. 或者  
  1028. uint(2)  
  1029. 把有符号整数2转换成无符号整数  
  1030.   
  1031.   
  1032. 整数和字符串之间的相互转换是很方便的  
  1033. 2.torstr  
  1034. “123”.toint  
  1035.   
  1036. R++系统库只定义了charint的相互转换  
  1037. char ch=`a  
  1038. putsl ch.toint  
  1039. 将输出97  
  1040. char ch=-1  
  1041. putsl ch.toint  
  1042. 将输出255  
  1043.   
  1044. 注意和标准C的区别,R++将char转换为int不会进行符号扩展  
  1045. 如果需要char和uint的相互转换请修改rsrc/char.h这个文件  
  1046.   
  1047. R++ v1.5开始支持自动类型转换:  
  1048.   
  1049. func(uint a)  
  1050. {  
  1051.     …  
  1052. }  
  1053.   
  1054. 那么  
  1055. func(2)  
  1056. 会自动替换为  
  1057. func(uint(2))  
  1058. 所以,请小心定义拷贝构造函数。  
  1059.   
  1060. 另外,和Java一样,R++推荐尽量少使用无符号数  
  1061.   
  1062.   
  1063.   
  1064.   
  1065.   
  1066.   
  1067.   
  1068.   
  1069.   
  1070. 24. 动态数组  
  1071.   
  1072. 特别注意下R++的命名规则:  
  1073.   
  1074. 类名和对象名不可以同名  
  1075. C++可以这样  
  1076. myclass myclass;  
  1077. R++则不能这样写  
  1078.   
  1079. 汇编的关键字不可以用来命名变量,但可以命名函数  
  1080. 比如vector类中有一个push函数,和汇编关键字push同名  
  1081. vector<int> a  
  1082. a.push(2)  
  1083. 是没问题的  
  1084.   
  1085.   
  1086. R++用rbuf代替STL的vector,用rstr代替string  
  1087. 用法大致是相同的。  
  1088. push就是vector的push_back  
  1089. cstr()就是string的c_str()  
  1090.   
  1091. 打开rsrc/basic.h可以找到  
  1092. #define vector rbuf  
  1093. #define string rstr  
  1094. 习惯于vector和string的朋友也可以使用这两个名字  
  1095.   
  1096. 要注意的是rbuf的size()方法和count()方法是不同的  
  1097. vector<int> a  
  1098. a.push(2)  
  1099. putsl a.size  
  1100. putsl a.count  
  1101. 输出是  
  1102. 4  
  1103. 1  
  1104. 即size返回所有元素占用的字节数,而count返回元素个数,这和STL是有区别的。  
  1105.   
  1106.   
  1107.   
  1108.   
  1109.   
  1110. 25. 多参数组  
  1111.   
  1112. R++没有静态数组,从V1.5开始也不再提供数组定义的语法糖,仅使用多参数组(数组运算符带多个参数)来实现多维数组:  
  1113. import “rbufm.h”  
  1114.   
  1115. main  
  1116. {  
  1117.     rbufm<int> arr(5,5)  
  1118.   
  1119.     for i=0;i<5;i++  
  1120.         for j=0;j<5;j++  
  1121.             arr[i,j].print  
  1122. }  
  1123.   
  1124. 下面是三维数组的例子:  
  1125. import ‘rbufm.h’  
  1126.   
  1127. main  
  1128. {  
  1129.     rbufm<int> arr(5,3,4)  
  1130.   
  1131.     for i=0 to 4  
  1132.         for j=0 to 2  
  1133.             for k=0 to 3  
  1134.                 arr[i,j,k].print  
  1135. }  
  1136.   
  1137. 需要注意的是,  
  1138. 如果将数组作为函数参数传递,会传递数组的拷贝,  
  1139. 也就是说,形参数组和实参数组互不影响。  
  1140.   
  1141. 如果形参数组和实参数组需要共享内存,  
  1142. 那么可以传递引用:  
  1143.   
  1144. func(rbufm<int>& arr)  
  1145. {  
  1146.     …  
  1147. }  
  1148.   
  1149.   
  1150. 当然也可以使用嵌套的模板来定义多维数组:  
  1151.   
  1152. main  
  1153. {  
  1154.     rbuf<rbuf<rbuf<int>>> arr  
  1155.   
  1156.     arr.alloc(5)  
  1157.     for i in arr  
  1158.     {  
  1159.         arr[i].alloc(3)  
  1160.         for j in arr[i]  
  1161.             arr[i][j].alloc(4)  
  1162.     }  
  1163.   
  1164.     for i in arr  
  1165.         for j in arr[i]  
  1166.             for k in arr[i][j]  
  1167.                 arr[i][j][k].print  
  1168. }  
  1169.   
  1170. 以上代码定义了一个5层3行4列的三维数组(未初始化输出的是随机值),  
  1171. 其中的for…in…也是一种语法糖  
  1172. for i in arr  
  1173. 等价于  
  1174. for(i=0;i<arr.count;i++)   
  1175.   
  1176.   
  1177. 另外,R++不支持重载小括号,但是可以重载中括号实现C++的函数对象(仿函数)。  
  1178.   
  1179.   
  1180.   
  1181.   
  1182.   
  1183.   
  1184.   
  1185.   
  1186.   
  1187.   
  1188.   
  1189.   
  1190. 26. 重载  
  1191.   
  1192. 不同类型指针作为函数参数传递时不需要强制转换,  
  1193. 因此仅有指针类型不同的函数不能重载,  
  1194. 下面的写法是有歧义的:  
  1195.   
  1196. func(int* a)  
  1197. {  
  1198.     …  
  1199. }  
  1200.   
  1201. func(char* a)  
  1202. {  
  1203.     …  
  1204. }  
  1205.   
  1206. 不同类型的引用可以重载:  
  1207.   
  1208. func(int& a)  
  1209. {  
  1210.     …  
  1211. }  
  1212.   
  1213. func(char& a)  
  1214. {  
  1215.     …  
  1216. }  
  1217.   
  1218. 相同类型的引用和非引用可以重载:  
  1219.   
  1220. func(int& a)  
  1221. {  
  1222.     …  
  1223. }  
  1224.   
  1225. func(int a)  
  1226. {  
  1227.     …  
  1228. }  
  1229.   
  1230. 下面是自定义结构体的例子:  
  1231.   
  1232. class A  
  1233. {  
  1234.     int* m_p  
  1235.       
  1236.     A()  
  1237.     {  
  1238.         m_p=r_new<int>5     
  1239.     }  
  1240.       
  1241.     A(A& a)  
  1242.     {  
  1243.         m_p=r_new<int>5  
  1244.         for i=0 to 4  
  1245.             m_p[i]=a.m_p[i]  
  1246.     }  
  1247.       
  1248.     ~A()  
  1249.     {  
  1250.         if(m_p!=null)  
  1251.         {  
  1252.             r_delete<int>m_p  
  1253.             m_p=null  
  1254.         }  
  1255.     }  
  1256.       
  1257.     operator=(A& a)  
  1258.     {  
  1259.         …  
  1260.     }  
  1261. }  
  1262.   
  1263.   
  1264. 自定义类型必须提供两个拷贝构造函数(一个引用一个非引用),如果没有非引用版本编译器会自动生成一个和引用版本函数体完全相同的函数,赋值函数也是一样。  
  1265.   
  1266.   
  1267. 另外,默认参数的重载规则和C++一样,不再赘述。  
  1268.   
  1269.   
  1270.   
  1271. 27. 指针和引用  
  1272.   
  1273. R++里引用不是“别名”,而是地址,  
  1274. 因此千万不要这样写:  
  1275. a=2  
  1276. int& b=a  
  1277. 正确的做法是:  
  1278. a=2  
  1279. int& b  
  1280. lea b,[ebp+s_off a]  
  1281.   
  1282. 其中s_off表示取得局部变量的偏移,  
  1283. 这样的话a与b共享同一段内存  
  1284. a.print  
  1285. b.print  
  1286. 将输出两个2  
  1287.   
  1288. 一般不会使用引用作为局部变量,  
  1289. 而是使用引用作为函数参数:  
  1290. func(a)  
  1291. a.print  
  1292. 函数func的定义是  
  1293. func(int& n)  
  1294. {  
  1295.     n=5  
  1296. }  
  1297.   
  1298.   
  1299. 指针的用法和标准C差不多:  
  1300. a=3  
  1301. p=&a  
  1302. a.print  
  1303. p->print  
  1304.   
  1305. 对于二重指针(或以上)则不可以使用类型推断:  
  1306. a=4  
  1307. p=&a  
  1308. int** pp=&p  
  1309.   
  1310. 对于三重指针(或以上)R++也没有定义语法糖,  
  1311. 因此需要使用嵌套的模板来定义:  
  1312. rp<rp<rp<int>>> ppp=&pp  
  1313. puts *(*(*ppp))  
  1314.   
  1315. 由此可以看到R++的指针实际上是模板类的一个实例,  
  1316. 具体情况请参考:  
  1317. rsrc/rp.h  
  1318.   
  1319.   
  1320.   
  1321. 28. 成员变量  
  1322.   
  1323. 举例说明:  
  1324.   
  1325. class A  
  1326. {  
  1327.     int m_a=2  
  1328.       int m_b(3)  
  1329.   
  1330.     A()  
  1331.     {  
  1332.     }  
  1333. }  
  1334.   
  1335. 即对于类的成员变量(数据成员),  
  1336. 可以直接在定义处初始化,  
  1337. 上面的例子等价于:  
  1338.   
  1339. class A  
  1340. {  
  1341.     int m_a  
  1342.   <span style=“white-space:pre”>    </span>int m_b  
  1343.   
  1344.     A()  
  1345.     {  
  1346.         m_a=2  
  1347.   <span style=“white-space:pre”>        </span>m_b=3  
  1348.     }  
  1349. }  
  1350.   
  1351. 即编译器会自动在该类的每一个构造函数的头部加上这么一个初始化语句  
  1352.   
  1353.   
  1354. 另外,R++将类的成员变量统一按1字节对齐:  
  1355.   
  1356. class A  
  1357. {  
  1358.     char m_a  
  1359.     int m_b  
  1360. }  
  1361.   
  1362. 这样的话  
  1363. sizeof A  
  1364. 将返回5。  
  1365.   
  1366. 注意这样写的话在x86上不会有问题,但移植到arm上就有问题了,  
  1367. 故不推荐这种写法。  
  1368.   
  1369.   
  1370. 如需使用全局变量请暂时先使用V1.1  
  1371.   
  1372.   
  1373.   
  1374. 29. 局部变量  
  1375.   
  1376. 和javascript一样,R++的局部变量在整个函数都是可见的,  
  1377. 比如:  
  1378. for(int i=0;i<10;i++)  
  1379. {  
  1380.     …  
  1381. }  
  1382. if(i>=10)  
  1383.     …  
  1384.       
  1385. 重复定义两个名字和类型都相同的变量也是允许的:  
  1386. vector<int> v  
  1387. v.push(2)  
  1388. v.count.print  
  1389.   
  1390. vector<int> v  
  1391. v.count.print  
  1392. 输出是  
  1393. 1 0  
  1394. 因为R++中定义变量会先析构后构造,  
  1395. 这表示将一个变量重新初始化。  
  1396.   
  1397. 故R++的类必须支持空构造函数(即使没有编译器也会自动生成一个),  
  1398. 并且需要在析构的时候判断是否已经析构:  
  1399.   
  1400. class A  
  1401. {  
  1402.     int* m_p  
  1403.       
  1404.     A()  
  1405.     {  
  1406.         m_p=r_new<int>5     
  1407.     }  
  1408.       
  1409.     ~A()  
  1410.     {  
  1411.         if(m_p!=null)  
  1412.         {  
  1413.             r_delete<int>m_p  
  1414.             m_p=null  
  1415.         }  
  1416.     }  
  1417. }  
  1418.   
  1419.   
  1420. 另外,可以使用赋值运算符进行类型推断:  
  1421. a=1+2  
  1422. 等价于  
  1423. auto a=1+2  
  1424. 等价于  
  1425. int a=1+2  
  1426.   
  1427.   
  1428.   
  1429.   
  1430.   
  1431.   
  1432.   
  1433. 30. 常量  
  1434.   
  1435. R++目前还不具备perl那种强大字符串处理能力,不过也向它学习了一些经验。  
  1436.   
  1437. 对于双引号扩起来的字符串,编译器并不会总是提供语法糖(请参考No.6),实际上它还是标准C的以0结尾的ASCII串。所以,为了方便处理,R++还提供了一个用单引号字符串:  
  1438. ‘123’  
  1439. 等价于  
  1440. rstr(“123”)  
  1441.   
  1442. 因此,单引号字符串完全可以当做内置字符串使用,转换成C字符串可以这样:  
  1443. ‘123’.cstr  
  1444.   
  1445. 连续两个反斜杠表示从当前位置到本行末均为单引号字符串,并且不进行转义:  
  1446. s=\\abc’123  
  1447. 等价于  
  1448. s=‘abc\’123’  
  1449. 主要用于字符串内部转义字符和引号很多的情况  
  1450.   
  1451.   
  1452. R++没有字符常量,但是提供一个反单引号:  
  1453. `a  
  1454. 等价于整数  
  1455. 97  
  1456.   
  1457. 如果整数比较长可以用下划线分隔:  
  1458. 1_0000_0000  
  1459. 等价于  
  1460. 100000000  
  1461.   
  1462. 以0x开头的常量表示16进制整型常量,以0b开头的常量表示二进制整型常量:  
  1463. 0xa  
  1464. 等价于  
  1465. 0b1010  
  1466. 等价于  
  1467. 10  
  1468.   
  1469. 双精度浮点(double)常量只有小数写法  
  1470. 比如  
  1471. 0.5  
  1472.   
  1473.   
  1474.   
  1475. 31. 控制结构  
  1476.   
  1477. 选择结构和大多数语言一样:  
  1478. if …  
  1479.     dosomething  
  1480. elif …  
  1481.     dosomething  
  1482. else  
  1483.     dosomething  
  1484.       
  1485. 对于省略花括号的情况,编译器会自动加上花括号;  
  1486. 对于花括号没有单独占一行的情况,编译器会自动换行,  
  1487. 要注意编译器提示的行号是自动处理之后的行号。  
  1488.   
  1489. 打开rsrc/basic.h可以找到  
  1490. #define elif else if  
  1491. 即elif是一个宏  
  1492.   
  1493. 条件结构是这样:  
  1494. switch …  
  1495. {  
  1496.     case 1  
  1497.     {  
  1498.         …  
  1499.     }  
  1500.     case 2  
  1501.     {  
  1502.         …  
  1503.     }  
  1504.     default  
  1505.     {  
  1506.         …  
  1507.     }  
  1508. }  
  1509. 或者  
  1510.   
  1511. switch …  
  1512. case 1  
  1513. {  
  1514.     …  
  1515. }  
  1516. case 2  
  1517. {  
  1518.     …  
  1519. }  
  1520. default  
  1521. {  
  1522.     …  
  1523. }  
  1524.   
  1525. R++的条件结构不需要break,  
  1526. case最后的冒号是可选的,但case后面的花括号是必须的。  
  1527. default是可选的,但如果有必须排在最后。  
  1528. (实际上目前R++还没有进行switch优化)  
  1529.   
  1530.   
  1531. 循环结构是这样:  
  1532.   
  1533. for i=1;i<10;i++  
  1534.     …  
  1535.   
  1536. for i<10  
  1537.     …  
  1538.       
  1539. 死循环有几种写法:  
  1540. for true  
  1541.     …  
  1542.       
  1543. for 1  
  1544.     …  
  1545.       
  1546. for  
  1547. {  
  1548.     …  
  1549. }  
  1550. 打开rsrc/basic.h可以找到  
  1551. #define while for  
  1552. whilefor是同义语,  
  1553. 是否赋初值和执行增量仅取决于条件表达式里面有没有分号  
  1554.   
  1555. 另外,R++有两种continue:  
  1556.   
  1557. continued  
  1558. 用于不执行增量直接进行条件判断  
  1559.   
  1560. continue  
  1561. 用于执行增量后再进行条件判断  
  1562.   
  1563.   
  1564. 至于break和大多数语言一样,不再赘述。  
  1565. (是否要增加关键字用于跳出多重循环是一个值得讨论的问题)  
  1566.   
  1567.   
  1568. 和Go语言一样,R++也抛弃了dowhile  
  1569. 下面是dowhile的等价写法:  
  1570.   
  1571. for  
  1572. {  
  1573.     …  
  1574.   
  1575.     ifn(i<10)  
  1576.         break  
  1577. }  
  1578.   
  1579. 或者  
  1580.   
  1581. for  
  1582. {  
  1583.     …  
  1584.   
  1585.     until(i>=10)  
  1586. }  
  1587.   
  1588. 其中ifn和until都是宏,请参考  
  1589. rsrc/basic.h  
  1590.   
  1591.   
  1592. R++的goto和jmp是同义语  
  1593. 下面是一个死循环  
  1594.   
  1595. F:  
  1596.     …  
  1597.     jmp F  
  1598.   
  1599. 如果你确定某个函数编译器没有自动换行,  
  1600. 可以直接在jmp后面加一个整数表示直接跳转到该行,  
  1601. 下面是直接跳转到第7行:  
  1602.   
  1603. …  
  1604. jmp 7  
  1605.   
  1606.   
  1607. 进行条件跳转也是很容易的:  
  1608.   
  1609. a<b  
  1610. jebxz G  
  1611.     putsl “a less than b”  
  1612. G:  
  1613.   
  1614. 等价于  
  1615.   
  1616. if(a<b)  
  1617.     putsl “a less than b”  
  1618.   
  1619.   
  1620. 注意只有下面几种类型的表达式可以作为条件表达式:  
  1621. 1: int  
  1622. 2: bool  
  1623. 3: 指针  
  1624. 4: 引用  
  1625.   
  1626. 如果使用其它类型作为条件表达式会在运行时出错。  
  1627.   
  1628.   
  1629. 另外R++不支持惰性求值  
  1630. (这是由于R++把一切运算符都作为函数处理)  
  1631. 而且R++会在非引用返回值传递引用的情况下增加临时变量,  
  1632. 所以不要依赖函数的传参顺序。  
  1633.   
  1634. 下面的写法是错误的:  
  1635. if p!=null && *p==2  
  1636.     …  
  1637.   
  1638. 可用等价的写法代替  
  1639. if p!=null  
  1640.     if *p==2  
  1641.         …  
  1642.           
  1643. 或者  
  1644. if p==null  
  1645.     return false  
  1646. if *p==2  
  1647.     …  
  1648.   
  1649.   
  1650.   
  1651. 32. 模板类  
  1652.   
  1653. 模板类用法与C++大致相同,  
  1654. 但是R++不支持template meta programming,暂时也不支持模板默认参数。  
  1655.   
  1656. class A<T>  
  1657. {  
  1658.     T m_a  
  1659.   
  1660.     A<T>  
  1661.     {  
  1662.     }  
  1663. }  
  1664.   
  1665. main  
  1666. {  
  1667.     A<int> a  
  1668.     A<A<int>> b  
  1669.   
  1670.     a.m_a.print  
  1671.     b.m_a.m_a.print  
  1672. }  
  1673.   
  1674. 注意模板类构造函数和析构函数写法和C++不同,后面必须有尖括号和模板参数。  
  1675.   
  1676.   
  1677. 关于模板类嵌套请参考  
  1678. example/32.2.h  
  1679.   
  1680.   
  1681.   
  1682. 33. 包  
  1683.   
  1684. R++中  
  1685. import  
  1686. 等价于  
  1687. #include  
  1688.   
  1689. 使用方法:  
  1690. import “A.h”  
  1691. 或者  
  1692. import ‘A.h’  
  1693.   
  1694. 注意不要这样写  
  1695. import <A.h>  
  1696. 因为R++不支持尖括号文件名  
  1697. 用双引号括起来的文件名R++会在当前目录和编译器目录搜索  
  1698.   
  1699. 和D语言、objective-c一样  
  1700. R++会自动处理重复包含的问题。  
  1701.   
  1702. R++中类和函数均不需要声明即可使用,  
  1703. 也就是说R++没有.cpp文件,只有.h文件  
  1704. (这样就不需要向前声明,递归引用也是允许的)  
  1705.   
  1706.   
  1707. (v1.5的“包”仍在开发中,如需使用“包”请暂时先使用v1.1)  
  1708.   
  1709.   
  1710. 另外,由于R++暂不支持类嵌套,因此命名空间里面也不可以包含类,  
  1711. 也不允许命名空间嵌套:  
  1712.   
  1713. namespace MY  
  1714. {  
  1715.     //R++没有枚举变量,只有枚举常量  
  1716.     enum  
  1717.     {  
  1718.         c_a=2  
  1719.         c_b=4  
  1720.     }  
  1721.   
  1722.     func  
  1723.     {  
  1724.         puts c_a  
  1725.     }  
  1726. }  
  1727.   
  1728. main  
  1729. {  
  1730.     MY.func  
  1731.     MY::func  
  1732.     puts MY.c_a  
  1733.     puts MY::c_b  
  1734. }  
  1735.   
  1736. 打开rsrc/basic.h可以找到  
  1737. #define namespace friend class  
  1738. 清楚地看到R++的命名空间不过是一个友元类,  
  1739. 或者可以称为静态类,即所有函数都没有this。  
  1740.   
  1741.   
  1742.   
  1743. 34. 堆  
  1744.   
  1745. R++的newdelete既不是运算符,也不是关键字,  
  1746. 而是模板函数:  
  1747.   
  1748. p=r_new<int>(5)  
  1749. 表示从堆中分配5个int  
  1750.   
  1751. r_delete<int>(p)  
  1752. 表示释放刚才分配的内存(不需要delete [] p)  
  1753.   
  1754. 一般来说,不需要使用newdelete,  
  1755. (一个很简单的原因,不用new就几乎不可能出现内存泄露)  
  1756. 推荐使用rbuf来进行内存分配:  
  1757. rbuf<int> a(5)  
  1758.   
  1759. 因为rbuf会在析构时自动释放内存,  
  1760. 当然也可以手动释放:  
  1761. a.free  
  1762.   
  1763. 可以使用数组运算符或者  
  1764. a.begin  
  1765. 或者  
  1766. a.m_p  
  1767. 访问刚才分配的内存  
  1768.   
  1769.   
  1770. 如果只需要一个对象,Java通常是这样:  
  1771. B b=new B();  
  1772. 而R++则这样:  
  1773. B b  
  1774. 一个对象通常在栈中分配就行了。  
  1775.   
  1776.   
  1777.   
  1778. 35. 数据结构  
  1779.   
  1780. R++内核非常小,  
  1781. 没有内置数据结构,甚至可以认为R++没有内置数据类型,  
  1782. 所有类型均在外部定义,这样做的好处是如果需要移植到64位,  
  1783. 编译器只要做很小的改动。  
  1784.   
  1785. 下面是以模板类的形式提供的几种数据结构:  
  1786.   
  1787. 名称  用途  
  1788. rbuf    动态数组,栈  
  1789. rstr    字符串  
  1790. rstrw   utf16字符串  
  1791. rlist   双向链表,队列,栈  
  1792. rset    红黑树  
  1793. rhash   哈希表  
  1794.   
  1795.   
  1796. 另外,rsrc/ralgo.h提供了快速排序、二分查找、split,  
  1797. 示例代码请参考  
  1798. example/35.x.h  
  1799.   
  1800.   
  1801.   
  1802. 36. 库  
  1803.   
  1804. R++解释器提供了一些常用函数,可以直接调用,  
  1805. 当然也可以使用封装好的类:  
  1806.   
  1807. 名称  用途  
  1808. rflie   文件操作  
  1809. rdir    遍历目录  
  1810. rsock   TCP套接字  
  1811. rmutex  互斥体  
  1812.   
  1813.   
  1814. 示例代码请参考  
  1815. example/36.x.h  
  1816.   
  1817.   
  1818.   
  1819. 37. 调试  
  1820.   
  1821. 如果语法通过而逻辑出错,  
  1822. 建议先使用注释和输出语句来确定出错的位置,  
  1823. 然后使用第16节的反射方法打印出相关函数的表达式语句和汇编代码。  
  1824.   
  1825. 此外,R++还提供一个性能分析器(参考黑客与画家),  
  1826. 请将rpp_debug.exe.rename改名为rpp_debug.exe  
  1827. 然后执行  
  1828. rpp_debug.exe x.h  
  1829. 运行完成以后会打印出所有函数的执行次数,以便找出性能瓶颈。  
  1830.   
  1831. 作为一个意外的发现,性能分析器还能检测出内存泄露,  
  1832. 只要简单看一下malloc和mfree这两个函数调用次数是否相同。  
  1833.   
  1834. 38. 文言文  
  1835.   
  1836. R++的标识符支持中文,  
  1837. 源文件支持GBK、Unicode(UTF16 little endian)以及UTF8三种编码。  
  1838. 使用R++来进行文言文编程看起来好像用处不大,  
  1839. 只是为了发挥DIY精神:  
  1840.   
  1841. 主即曰‘你好’也  
  1842.   
  1843. 上面是中文编程的第一个例子,打印“你好”。  
  1844.   
  1845. 运行这个例子之前请先设置控制台(因为R++的字符串常量使用UTF8)  
  1846. chcp 65001  
  1847. 然后在命令行标题栏上点击右键,选择“属性”->“字体”,将字体修改为True Type字体“Lucida Console”,再点击确定将属性应用到当前窗口。  
  1848.    
  1849. 然后删除rinf/optr.txt  
  1850. 再将rinf/optr2.txt重命名为rinf/optr.txt  
  1851. 最后反注释rsrc/basic.h中的第一行:   
  1852. import “chs.h”  
  1853.   
  1854. 下面再看一个递归求和的例子:  
  1855.   
  1856. 主即曰和 100也  
  1857.    
  1858. 数和 数甲 即  
  1859. 若甲 小于2  
  1860. 归1  
  1861. 归甲 加和 甲 减1  
  1862. 也  
  1863.   
  1864. 注意有些地方要用空格分隔,  
  1865. 更多的例子请参考:  
  1866. example/38.x.h  
  1867.   
  1868.   
  1869.   
  1870.   
  1871. </span>  

标签