怎样将CList变量在函数间传递呀?

sjerpy 2006-02-17 01:02:37
怎样将CList<int,int>定义的变量在函数间传递呀?
...全文
130 8 打赏 收藏 转发到动态 举报
写回复
用AI写文章
8 条回复
切换为时间正序
请发表友善的回复…
发表回复
syy64 2006-02-20
  • 打赏
  • 举报
回复

ShowList(CList& mList);
YufengShi 2006-02-20
  • 打赏
  • 举报
回复
void ShowList(const CList<int,int>& mList); //原型
ShowList(mList); //函数调用
sjerpy 2006-02-20
  • 打赏
  • 举报
回复
假如我有一个变量 CList<int,int> mList;
函数名 ShowList(??????);想把mList 传进函数,该怎么写呀
sjerpy 2006-02-20
  • 打赏
  • 举报
回复
那我该怎么定义函数呢 ,能否举个例呢?
liuzunde 2006-02-17
  • 打赏
  • 举报
回复
引用
syy64 2006-02-17
  • 打赏
  • 举报
回复
应该用引用。
huaren801007 2006-02-17
  • 打赏
  • 举报
回复
引用
flyelf 2006-02-17
  • 打赏
  • 举报
回复
用指针或者引用
JAVA模版引擎Freemarker常用标签(一) 1. if指令 这是一个典型的分支控制指令,该指令的作用完全类似于Java语言中的if,if指令的语法格式如下: ... ... ... ... 例子如下: 60)>老年人 40)>中年人 20)>青年人 少年人 输出结果是:青年人 上面的代码中的逻辑表达式用括号括起来主要是因为里面有>符号,由于FreeMarker会将>符号当成标签的结束字符,可能导致程序出错,为了避免这种情况,我们应该在凡是出现这些符号的地方都使用括号. <#if animals.python.price Pythons are cheaper than elephants today. Pythons are not cheaper than elephants today. 2、 switch , case , default , break指令 这些指令显然是分支指令,作用类似于Java的switch语句,switch指令的语法结构如下: ... ... ... 3、 list, break指令 list指令是一个迭代输出指令,用于迭代输出数据模型中的集合,list指令的语法格式如下: ... 上面的语法格式中,sequence就是一个集合对象,也可以是一个表达式,但该表达式将返回一个集合对象,而item是一个任意的名字,就是被迭代输出的集合元素.此外,迭代集合对象时,还包含两个特殊的循环变量: item_index:当前变量的索引值 item_has_next:是否存在下一个对象 也可以使用指令跳出迭代 例子如下: ${x_index + 1}.${x}, We have these animals: NamePrice ${being.name}${being.price} Euros 输出为: We have these animals: NamePrice mouse50 Euros elephant5000 Euros python4999 Euros 4、include指令 include指令的作用类似于JSP的包含指令,用于包含指定页.include指令的语法格式如下: 在上面的语法格式中,两个参数的解释如下: filename:该参数指定被包含的模板文件 options:该参数可以省略,指定包含时的选项,包含encoding和parse两个选项,其中encoding指定包含页面时所用的解码集,而parse指定被包含文件是否作为FTL文件来解析,如果省略了parse选项值,则该选项默认是true. Test page Test page Blah blah... 5、 import指令 该指令用于导入FreeMarker模板中的所有变量,并将该变量放置在指定的Map对象中,import指令的语法格式如下: 上面的代码将导入/lib/common.ftl模板文件中的所有变量,将这些变量放置在一个名为com的Map对象中. 创建库 ? 下面是一个创建库的例子(假设保存在lib/my_test.ftl中): Copyright (C) ${date} Julia Smith. All rights reserved. Email: ${mail} ? 使用import指令导入库到模板中,Freemarker会为导入的库创建新的名字空,并可以通过import指令中指定的散列变量访问库中的变量: ${my.mail} ${mail} 输出结果: Copyright (C) 1999-2002 Julia Smith. All rights reserved. Email: jsmith@acme.com jsmith@acme.com fred@acme.com 可以看到例子中使用的两个同名变量并没有冲突,因为它们位于不同的名字空 l 可以使用assign指令在导入的名字空中创建或替代变量,下面是一个例子: ${my.mail} ${my.mail} l 输出结果: jsmith@acme.com jsmith@other.com l 数据模型中的变量任何地方都可见,也包括不同的名字空,下面是修改的库: Copyright (C) ${date} ${user}. All rights reserved. l 假设数据模型中的user变量的值是Fred,则下面的代码: ${my.mail} l 输出结果: Copyright (C) 1999-2002 Fred. All rights reserved. 1.6 算术运算符 FreeMarker表达式中完全支持算术运算,FreeMarker支持的算术运算符包括:+, - , * , / , % 看如下的代码: ${ x * x - 100 } ${ x /2 } ${ 12 } 输出结果是: -75 2.5 2 在表达式中使用算术运算符时要注意以下几点: 1,运算符两边的运算数字必须是数字 2,使用+运算符时,如果一边是数字,一边是字符串,就会自动将数字转换为字符串再连接,如:${3 + "5"},结果是:35 使用内建的int函数可对数值取整,如: ${ (x/2)?int } ${ 1.1?int } ${ 1.999?int } ${ -1.1?int } ${ -1.999?int } 结果是:2 1 1 -1 -1 1.7 比较运算符 表达式中支持的比较运算符有如下几个: 1. =或者==:判断两个值是否相等. 2. !=:判断两个值是否不等. 3. >或者gt:判断左边值是否大于右边值 4. >=或者gte:判断左边值是否大于等于右边值 5. <或者lt:判断左边值是否小于右边值 6. 会有更好的效果,因为 FreeMarker会把>解释成FTL标签的结束字符,当然,也可以使用括号来避免这种情况,如:y)> 1.8 逻辑运算符 逻辑运算符有如下几个: 逻辑与:&& 逻辑或:|| 逻辑非:! 逻辑运算符只能作用于布尔值,否则将产生错误 1.9 内建函数 FreeMarker还提供了一些内建函数来转换输出,可以在任何变量后紧跟?,?后紧跟内建函数,就可以通过内建函数来轮换输出变量.下面是常用的内建的字符串函数: html:对字符串进行HTML编码 cap_first:使字符串第一个字母大写 lower_case:将字符串转换成小写 upper_case:将字符串转换成大写 trim:去掉字符串前后的空白字符 下面是集合的常用内建函数 size:获取序列中元素的个数 下面是数字值的常用内建函数 int:取得数字的整数部分,结果带符号 例如: ${test?html} ${test?upper_case?html} 结果是:Tom & Jerry TOM & JERRY 1.10 空值处理运算符 FreeMarker对空值的处理非常严格,FreeMarker的变量必须有值,没有被赋值的变量就会抛出异常,因为FreeMarker未赋值 的变量强制出错可以杜绝很多潜在的错误,如缺失潜在的变量命名,或者其他变量错误.这里所说的空值,实际上也包括那些并不存在的变量,对于一个Java的 null值而言,我们认为这个变量是存在的,只是它的值为null,但对于FreeMarker模板而言,它无法理解null值,null值和不存在的变 量完全相同. 为了处理缺失变量,FreeMarker提供了两个运算符: !: 指定缺失变量的默认值 ??: 判断某个变量是否存在 其中,!运算符的用法有如下两种: variable!或variable!defaultValue,第一种用法不给缺失的变量指定默认值,表明默认值是空字符串,长度为0的集合,或者长度为0的Map对象. 使用!指定默认值时,并不要求默认值的类型和变量类型相同.使用??运算符非常简单,它总是返回一个布尔值,用法为:variable??,如果该变量存在,返回true,否则返回false ########################### 最常用的概念 1、 scalars:存储单值 字符串:简单文本由单或双引号括起来。 数字:直接使用数值。 日期:通常从数据模型获得 布尔值:true或false,通常在标记中使用 2、 hashes:充当其它对象的容器,每个都关联一个唯一的查询名字 具有一个唯一的查询名字和他包含的每个变量相关联。 3、 sequences:充当其它对象的容器,按次序访问 使用数字和他包含的每个变量相关联。索引值从0开始。 4、 集合变量: 除了无法访问它的大小和不能使用索引来获得它的子变量:集合可以看作只能由指令使用的受限sequences。 5、 方法:通过传递的参数进行计算,以新对象返回结果 方法变量通常是基于给出的参数计算值在数据模型中定义。 6、 用户自定义FTL指令:宏和变换器 7、 节点 节点变量表示为树型结构中的一个节点,通常在XML处理中使用。 在模板里对sequences和hashes初始化 sequences 1. [“you”,”me”,”he”] 2. 1..100 3. [ {“Akey”:”Avalue”},{“Akey1”:”Avalue1”}, {“Bkey”:”Bvalue”},{“Bkey1”:”Bvalue1”}, ] hashes {“you”:”a”,”me”:”b”,”he”:”c”} 注释标志 旧版本的freemarker采用的是 注释 方法 sequences内置方法 sequence?first 返回sequence的第一个值;前提条件sequence不能是null sequence?last 返回sequence最后一个值 sequence?reverse 反转sequence的值 sequence?size 返回sequence的大小 sequence?sort 对sequence按里面的对象toString()的结果进行排序 sequence?sort_by(value) 对sequence 按里面的对象的属性value进行排序 如: sequence里面放入的是10 个user对象,user对象里面包含name,age等属性 sequence?sort_by(name) 表示所有的user按user.name进行排序 hashes内置方法 hash?keys 返回hash里的所有keys, 返回结果类型sequence hash?values 返回hash里的所有value, 返回结果类型sequence 模板 使用FTL(freeMarker模板语言)编写 组成部分 一、整体结构 1、注释:,不会输出。 2、文本:直接输出。 3、interpolation:由 ${var} 或 #{var} 限定,由计算值代替输出。 4、FTL标记 二.表达式 1、直接指定值: 1-1、字符串: 由双引号或单引号括起来的字符串,其中的特殊字符(如' " \等)需要转义。 1-2、raw字符串: 有一种特殊的字符串称为raw字符串,被认为是纯文本,其中的\和{等不具有特殊含义,该类字符串在引号前面加r,下面是一个例子: ${r"/${data}"year""}屏幕输出结果为:/${data}"year" 转义 含义 序列 \" 双引号(u0022) \' 单引号(u0027) \\ 反斜杠(u005C) \n 换行(u000A) \r Return (u000D) \t Tab (u0009) \b Backspace (u0008) \f Form feed (u000C) \l \a & \{ { \xCode 4位16进制Unicode代码 1-3、数字:直接输入,不需要引号 1)、精度数字使用“.”分隔,不能使用分组符号 2)、目前版本不支持科学计数法,所以“1E3”是错误的 3)、不能省略小数点前面的0,所以“.5”是错误的 4)、数字8、+8、08和8.00都是相同的 1-4、布尔值:true和false,不使用引号 1-5、序列:由逗号分隔的子变量列表,由[]方括号限定。 1)、子变量列表可以是表达式 2)、可以使用数字范围定义数字序列,不需要方括号限定,例如2..5等同于[2, 3, 4, 5],但是更有效率,可以定义反递增范围如:5..2。 1-6、散列(hash) 1)、由逗号分隔的键/值列表,由{}大括号限定,键和值之用冒号分隔,如:{"key1":valu1,"key2":"character string"....} 2)、键和值都是表达式,但是键必须是字符串。 2、获取变量: 2-1、顶层变量:${变量名} 变量名只能是字母、数字、下划线、$、#、@ 的组合,且不能以数字开头。 2-2、散列:有两种方法 1)、点语法:变量名字和顶层变量的名字受同样的限制 2)、方括号语法:变量名字无限制,可以是任意的表达式的结果 book.author.name book.author.["name"] book["author"].name book["author"]["name"] 以上是等价的。 2-3、序列:使用散列的方括号语法获取变量,方括号中的表达式结果必须为数字。注意:第一个项目的索引为0。可以使用 [startindex..endindex]语法获取序列片段。 2-4、特殊变量:FreeMarker内定义变量,使用.variablename语法访问。 3、字符串操作 3-1、interpolation:使用${}或#{}在文本部分插入表达式的值,例如: ${"hello${username}!"} ${"${username}${username}${username}"} 也可以使用+来获得同样的结果: ${"hello"+username+"!"} ${username+username+username} 注意:${}只能用于文本部分而不能出现于标记内。 或都是错误的; 是正确的。 本例中user.login的值必须是布尔类型。 3-2、子串: 举例说明:假如user的值为"Big Joe" ${user[0]}${user[4]}结果是:BJ ${user[1..4]}结果是:ig J 4、序列操作 4-1、连接操作:可以使用+来操作,例如: ["title","author"]+["month","day"] 5、散列操作 5-1、连接操作:可以使用+来操作,如果有相同的KEY,则右边的值会替代左边的值,例如: {"title":散列,"author":"emma"}+{"month":5,"day":5}+{"month":6}结果month的值就是6。 6、算术运算 6-1、操作符:+、-、*、/、% 除+号以外的其他操作符两边的数据,必须都是数字类型。 如果+号操作符一边有一个字符型数据,会自动将另一边的数据转换为字符型数据,运算结果为字符型数据。 6-2、比较操作符: 1}、= 2}、== 3}、!= 4}、< 5}、 7}、>= 1-3的操作符,两边的数据类型必须相同,否则会产生错误 4-7的操作符,对于日期和数字可以使用,字符串不可以使用。 注意: 1}、FreeMarker是精确比较,所以"x" "x " "X"是不等的。 2}、因为对FTL来说是开始和结束标记,所以,可以用两种方法来避免这种情况: 一种是使用括号<#if (a 另一是使用替代输出,对应如下: < lt gt >= gte 6-3、逻辑操作符:只能用于布尔值,否则会出现错误。 &&(and)与运算 ||(or)或运算 !(not)非运算 6-4、内建函数:使用方法类似于访问散列的子变量,只是使用?代替.例如:${test?upper_case?html} 常用的内建函数列举如下: 1}、字符串使用: html:对字符串进行HTML编码 cap_first:字符串第一个字母大写 lower_first:字符串第一个字母小写 upper_case:将字符串转换成大写 trim:去掉字符前后的空白字符 2)、序列使用: size:获得序列中元素的数目 3)、数字使用: int:取得数字的整数部分 7、操作符的优先顺序: 后缀:[subbarName][subStringRange].(mathodParams) 一元:+expr、-expr、! (not) 内建:? 乘法:*、/、% 加法:+、- 关系:<、、>= (lt、lte、gt、gte) 相等:=、==、!= 逻辑与:&& (and) 逻辑或:|| (or) 数字范围:.. 四、interpolation inperpolation只能用于文本,有两种类型:通用interpolation及数字interpolation 1、通用interpolation 如${expr} 1-1、插入字符串值:直接输出表达式结果。 1-2、插入数字值:根据缺省格式(由setting指令设置)将表达式结果转换成文本输出;可以使用内建函数string来格式化单个interpolation 如: ${answer} ${answer?string} ${answer?string.number} ${answer?string.currency} ${answer?string.percent} 1-3、插入日期值:根据缺省格式(由setting指令设置)将表达式结果转换成文本输出;可以使用内建函数string来格式化单个interpolation 如: ${lastupdata?string("yyyy-MM-dd HH:mm:ss zzzz")} ${lastupdata?string("EEE,MMM d, ''yy")} ${lastupdata?string("EEEE,MMMM dd, yyyy,hh:mm:ss a '('zzz')'")} 1-4、插入布尔值:根据缺省格式(由setting指令设置)将表达式结果转换成文本输出;可以使用内建函数string来格式化单个interpolation 如: ${foo?string("yes","no")} 2、数字interpolation: 有两种形式: 1)、#{expr} 2)、#{expr;format}:format可以用来格式化数字,format可以是如下: mX:小数部分最小X位 MX:小数部分最大X位 例如: #{x;M2} #{y;M2} #{x;m1} #{y;m1} #{x;m1M2} #{y;m1M2} 宏 宏和变换器变量是两种不同类型的用户自定义指令,他们的区别是: 宏可以在模板中用macro指令来定义 变换器是在模板外由程序定义 1、宏:和某个变量关联的模板片段,以便在模板中通过用户自定义指令使用该变量 1-1、基本用法: 例如: Hello JOE! 使用时: 如果没有体内容也可以用 1-2、变量: 1)、可以在宏定义之后定义参数,宏参数是局部变量,只在宏定义中有效。如: Hello ${person}! 使用时: and 输出为: Hello emma! Hello LEO! 注意:宏的参数是FTL表达式,所以,person=emma和上面的例子中具有不同的意义,这意味着将变量emma的值传给person,这个值可能是任意一种数据类型,甚至是一个复杂的表达式。 宏可以有多个参数,使用时参数的次序是无关的,但是只能使用宏中定义的参数,并且对所有参数赋值。如: Hello ${person}! 使用时: 正确 错误,color没有赋值,此时,如果在定义宏时为color定义缺省值这样的话,这个使用方法就是正确的。 错误,宏greet定义中未指定bgcolor这个参数 2、嵌套内容: 2-1、自定义指令可以有嵌套内容,使用指令,执行自定义指令开始和结束标记之的模板片段。例如: hello Emma! 输出为 hello Emma! 2-2、指令可以被多次调用,例如 hello Emma! 输出为 hello Emma! hello Emma! hello Emma! hello Emma! 2-3、嵌套的内容可以是有效的FTL,例如: Hello ${person}! 输出为: Hello Emma! Hello Andrew! Hello Peter! 2-4、宏定义中的局部变量对嵌套内容是不可见的,例如: ${y}${count}/${x}: ${y?default("?")} ${x?default("?")} ${count?default("?")} 输出结果为 test 3/1:??? test 3/2:??? test 3/3:??? 2-5、在宏定义中使用循环变量,通常用来重复嵌套内容,基本用法为:作为nested指令的参数,传递循环变量的实际值,而在调用自定义指令时,在标记的参数后面指定循环变量的名字。 例如: ${c}. ${halfc} last! 输出结果是 1. 0.5 2. 1 3. 1.5 4. 2last! 注意:指定循环变量的数目和用户定义指令开始标记指定的不同不会有问题 调用时,少指定循环变量,多指定的值会不见 调用时,多指定循环变量,多余的循环变量不会被创建 二、在模板中定义变量 1、在模板中定义的变量有三种类型 1-1、plain变量:可以在模板的任何地方访问,包括使用include指令插入的模板,使用assign指令创建和替换。 1-2、局部变量:在宏定义体中有效,使用local指令创建和替换。 1-3、循环变量:只能存在于指令的嵌套内容,由指令(如list)自动创建。 注意: 1)、宏的参数是局部变量,不是循环变量。 2)、局部变量隐藏同名的plain变量 3)、循环变量隐藏同名的plain变量和局部变量。 例如: 1. ${x} 6. ${x} 7. ${x} 8. ${x} 9. ${x} 2. ${x} 3. ${x} 4. ${x} 5. ${x} 4)、内部循环变量隐藏同名的外部循环变量 ${x} ${x} ${x} ${x} ${x} 5)、模板中的变量会隐藏数据模型中的同名变量,如果需访问数据模型中的变量,使用特殊变量global。 例如: 假设数据模型中的user值为Emma ${user} ${.global.user} macro, nested, return 语法 ... ... ... 用例 Test text, and the params: ${foo}, ${bar}, ${baaz} 输出 Test text, and the params: a, b, 23 Test text, and the params: a, b, -1 Test text, and the params: a, Bar, 23 Test text, and the params: a, Bar, -1 定义循环输出的宏 ${title?cap_first}: ${x?cap_first} 输出结果 Animals: 包含body的宏 ${c}. ${halfc} Last! 输出 1. 0.5 2. 1 3. 1.5 4. 2 Last! t, lt, rt 语法 去掉左右空白和回车换行 去掉左边空白和回车换行 去掉右边空白和回车换行 取消上面的效果 B指令 freemarker指令有两种: 1、预定义指令:引用方式为 2、用户定义指令:引用方式为,引用用户定义指令时须将#换为@。 注意:如果使用不存在的指令,FreeMarker不会使用模板输出,而是产生一个错误消息。 freemarker指令由FTL标记来引用,FTL标记和HTML标记类似,名字前加#来加以区分。如HTML标记的形式为则FTL标记的形式是(此处h1标记和list指令没有任何功能上的对应关系,只是做为说明使用一下)。 有三种FTL标记: 1)、开始标记: 2)、结束标记: 3)、空标记: 注意: 1) FTL会忽略标记之中的空格,但是,<#和指令 与 间不能有空格。 2) FTL标记不能够交叉,必须合理嵌套。每个开始标记对应一个结束标记,层层嵌套。 如: ${数据} game over! 注意事项: 1)、FTL对大小写敏感。所以使用的标记及interpolation要注意大小写。name与NAME就是不同的对象。是正确的标记,而则不是。 2)、interpolation只能在文本部分使用,不能位于FTL标记内。如是错误的,正确的方法是:,而且此处var必须为布尔值。 3)、FTL标记不能位于另一个FTL标记内部,注释例外。注释可以位于标记及interpolation内部。 if, else, elseif 语法 ... ... ... ... ... 用例 x is 1 x is 1 x is not 1 We have these animals: NamePrice ${being.name} ${being.price} Euros It is Big Joe It is not Big Joe switch, case, default, break 语法 ... ... ... ... 用例 字符串 This will be processed if it is small This will be processed if it is medium This will be processed if it is large This will be processed if it is neither 数字 1 2 d 如果x=1 输出 1 2, x=2输出 2, x=3 输出d list, break 语法 ... ... 关键字 item_index:是list当前值的下标 item_has_next:判断list是否还有值 用例 ${x_index + 1}. ${x}, 输出 1. winter, 2. spring, 3. summer, 4. autumn include 语法 or options包含两个属性 encoding=”GBK” 编码格式 parse=true 是否作为ftl语法解析,默认是true,false就是以文本方式引入.注意在ftl文件里布尔值都是直接赋值的如parse=true,而不是parse=”true” 用例 /common/copyright.ftl包含内容 Copyright 2001-2002 ${me} All rights reserved. 模板文件 Some test Yeah. 输出结果 Some test Yeah. Test page Test page Blah blah... Import 语法 类似于java里的import,它导入文件,然后就可以在当前文件里使用被导入文件里的宏组件 用例 假设mylib.ftl里定义了宏copyright那么我们在其他模板页面里可以这样使用 "my"在freemarker里被称作namespace compress 语法 ... 用来压缩空白空和空白的行 用例 ( 1 2 3 4 5 ${moo} test only I said, test only ) 输出 (1 2 3 4 5 moo test only I said, test only) escape, noescape 语法 ... ... ... 用例 主要使用在相似的字符串变量输出,比如某一个模块的所有字符串输出都必须是html安全的,这个时候就可以使用该表达式 First name: ${firstName} Last name: ${lastName} Maiden name: ${maidenName} 相同表达式 First name: ${firstName?html} Last name: ${lastName } Maiden name: ${maidenName?html} assign 语法 or or or capture this or capture this 用例 生成变量,并且给变量赋值 给seasons赋予序列值 给变量test加1 给my namespage 赋予一个变量bgColor,下面可以通过my.bgColor来访问这个变量 将一段输出的文本作为变量保存在x里 下面的阴影部分输出的文本将被赋值给x ${n} Number of words: ${x?word_list?size} ${x} Hello ${user}! error true 同时也支持中文赋值,如: java ${语法} 打印输出: java global 语法 or or capture this 全局赋值语法,利用这个语法给变量赋值,那么这个变量在所有的namespace中是可见的,如果这个变量被当前的assign语法覆盖如 在当前页面里x=2将被隐藏,或者通过${.global.x}来访问[A2] setting 语法 用来设置整个系统的一个环境 locale number_format boolean_format date_format, time_format, datetime_format time_zone classic_compatible 用例 假如当前是匈牙利的设置,然后修改成美国 ${1.2} ${1.2} 输出 1,2 1.2 因为匈牙利是采用“,”作为十进制的分隔符,美国是用“.” C一些常用方法或注意事项 表达式转换类 ${expression}计算expression并输出 #{ expression }数字计算#{ expression ;format}安格式输出数字format为M和m M表示小数点后最多的位数,m表示小数点后最少的位数如#{121.2322;m2M2}输出121.23 数字循环 1..5 表示从1到5,原型number..number 对浮点取整数 ${123.23?int} 输出123 给变量默认值 ${var?default(“hello world”)?html}如果var is null那么将会被hello world替代 判断对象是不是null Mouse found 也可以直接${mouse?if_exists})输出布尔形 常用格式化日期 openingTime必须是Date型,详细查看freemarker文档 Reference->build-in referece->build-in for date ${openingTime?date} ${openingTime?date_time} ${openingTime?time} 添加全局共享变量数据模型 在代码里的实现 cfg = Configuration.getDefaultConfiguration(); cfg.setSharedVariable("global", "you good"); 页面实现可以通过global指令,具体查看指令里的global部分 直接调用java对象的方法 ${object.methed(args)} 字符串处理(内置方法) html安全输出 “abcsdfsf”?html 返回安全的html输出,替换掉html代码 xml安全输出 var?xml substring的用法 ${user[0]}${user[4]} ${user[1..4]} 输出 : ho ello 类似String.split的用法 “abc;def;ghi”?split(“;”)返回sequence 将字符串按空格转化成sequence,然后取sequence的长度 var?word_list 效果同 var?split(“ ”) var?word_list?size 取得字符串长度 var?length 大写输出字符 var?upper_case 小写输出字符 var?lower_case 首字符大写 var?cap_first 首字符小写 var?uncap_first 去掉字符串前后空格 var?trim 每个单词的首字符大写 var?capitalize 类似String.indexof: “babcdabcd”?index_of(“abc”) 返回1 “babcdabcd”?index_of(“abc”,2) 返回5 类似String.lastIndexOf last_index_of和String.lastIndexOf类似,同上 下面两个可能在代码生成的时候使用(在引号前加”\”) j_string: 在字符串引号前加”\” String BEAN_NAME = "${beanName?j_string}"; 打印输出: String BEAN_NAME = "The \"foo\" bean."; js_string: alert("Welcome ${user}!"); 打印输出: alert("Welcome Big Joe\'s \"right hand\"!"); 替换字符串 replace${s?replace(‘ba’, ‘XY’ )} ${s?replace(‘ba’, ‘XY’ , ‘规则参数’)}将s里的所有的ba替换成xy 规则参数包含:i r m s c f具体含义如下: ·i: 大小写不区分. ·f: 只替换第一个出现被替换字符串的字符串 ·r: XY是正则表达式 ·m: Multi-line mode for regular expressions. In multi-line mode the expressions ^ and $ match just after or just before, respectively, a line terminator or the end of the string. By default these expressions only match at the beginning and the end of the entire string. ·s: Enables dotall mode for regular expressions (same as Perl singe-line mode). In dotall mode, the expression . matches any character, including a line terminator. By default this expression does not match line terminators. ·c: Permits whitespace and comments in regular expressions. D freemarker在web开发中注意事项 freemarker与webwork整合 web中常用的几个对象 Freemarker的ftl文件中直接使用内部对象: ${Request ["a"]} ${RequestParameters["a"]} ${Session ["a"]} ${Application ["a"]} ${JspTaglibs ["a"]} 与webwork整合之后 通过配置的servlet 已经把request,session等对象置入了数据模型中 在view中存在下面的对象 我们可以在ftl中${req}来打印req对象 req - the current HttpServletRequest res - the current HttpServletResponse stack - the current OgnlValueStack ognl - the OgnlTool instance webwork - an instance of FreemarkerWebWorkUtil action - the current WebWork action exception - optional the Exception instance, if the view is a JSP exception or Servlet exception view view中值的搜索顺序 ${name}将会以下面的顺序查找name值 freemarker variables value stack request attributes session attributes servlet context attributes 在模板里ftl里使用标签 注意,如果标签的属性值是数字,那么必须采用nubmer=123方式给属性赋值 JSP页面 Keyword: Exclude: 模板ftl页面 Keyword: Exclude: 如何初始化共享变量 1. 初始化全局共享数据模型 freemark在web上使用的时候对共享数据的初始化支持的不够,不能在配置初始化的时候实现,而必须通过ftl文件来初始化全局变量。这是不能满主需求的,我们需要在servlet init的时候留出一个接口来初始化系统的共享数据 具体到和webwork整合,因为本身webwork提供了整合servlet,如果要增加全局共享变量,可以通过修改com.opensymphony.webwork.views.freemarker.FreemarkerServlet来实现,我们可以在这个servlet初始化的时候来初始化全局共享变量 与webwork整合配置 配置web.xml freemarker com.opensymphony.webwork.views.freemarker.FreemarkerServlet TemplatePath / NoCache true ContentType text/html template_update_delay 0 default_encoding GBK number_format 0.########## 1 freemarker *.ftl E高级方法 自定义方法 ${timer("yyyy-MM-dd H:mm:ss", x)} ${timer("yyyy-MM-dd ", x)} 在模板中除了可以通过对象来调用方法外(${object.methed(args)})也可以直接调用java实现的方法,java类必须实现接口TemplateMethodModel的方法exec(List args). 下面以把毫秒的时转换成按格式输出的时为例子 public class LongToDate implements TemplateMethodModel { public TemplateModel exec(List args) throws TemplateModelException { SimpleDateFormat mydate = new SimpleDateFormat((String) args.get(0))); return mydate.format(new Date(Long.parseLong((String)args.get(1))); } } 将LongToDate对象放入到数据模型中 root.put("timer", new IndexOfMethod()); ftl模板里使用 ${timer("yyyy-MM-dd H:mm:ss", x)} ${timer("yyyy-MM-dd ", x)} 输出 2001-10-12 5:21:12 2001-10-12 自定义 Transforms 实现自定义的文本或表达式的功能,允许对中的最终文本进行解析转换 例子:实现str 将str转换成STR 的功能 代码如下: import java.io.*; import java.util.*; import freemarker.template.TemplateTransformModel; class UpperCaseTransform implements TemplateTransformModel { public Writer getWriter(Writer out, Map args) { return new UpperCaseWriter(out); } private class UpperCaseWriter extends Writer { private Writer out; UpperCaseWriter (Writer out) { this.out = out; } public void write(char[] cbuf, int off, int len) throws IOException { out.write(new String(cbuf, off, len).toUpperCase()); } public void flush() throws IOException { out.flush(); } public void close() { } } } 然后将此对象put到数据模型中 root.put("upcase", new UpperCaseTransform()); 在view(ftl)页面中可以如下方式使用 hello world 打印输出: HELLO WORLD F.Built-ins ${x?upper_case} – 小写变大写 ${test?html} - 转换为HTML编码格式 ${repeat("A", B)} – 复制B次A Example: ${test?html} ${test?upper_case?html} Assuming that test stores the string ``Tom & Jerry'', the output will be: Tom & Jerry TOM & JERRY --------- ${repeat("What", 3)} will print: :WhatWhatWhat 1. String内置的JavaScript转换: js_string 用途:用于JavaScript转义,转换',",换行等特殊字符 模板: alert("${errorMessage?js_string}"); 输出: alert("Readonly\'s pet name is \"Cross Bone\""); 2.内置的默认值处理:default 用途: 用于处理默认值 模本: User: ${userLogin.name?default("Anonymous")} ${(employee.department.manager.name)?default(" ")} 输出: User: Anonymous 注,可以对整个对象树加上(),再用内置处理器这种方便的做法,偶也是最近刚学会的,以前一直用很傻的方法做..... 3. Sequence内置的计数器: xxx_index 用途:显示序号 模板: ${e_index}. ${e.name} 输出: 1. Readonly 2. Robbin 4. Sequence内置的分段器: chunk 用途:某些比较BT的排版需求 模板: ${cell} ${cell} 输出: a b c d e f g h i j a b c d e f g h i j - - String ${"It's \"quoted\" and this is a backslash: \\"} ${'It\'s "quoted" and this is a backslash: } ${r"${foo}"} raw字符串,原封不动地现实引号中的内容 ps:前一种是用双引号来引用字符串,后一种是用单引号来引用字符串。 分别需要对双引号和单引号进行转义 ${"${user}${user}${user}${user}"} ${user + user + user + user} 效果相同 ★substring ${user[0]}${user[4]} ${user[1..4]} ${user[4..]} ★number 不支持科学计数法 小数点前面的零不能省略 ★sequences ${x} ${x} ${x} 数组的拼接 - ${user} ★hash - Joe is ${ages.Joe} - Fred is ${ages.Fred} - Julia is ${ages.Julia} 注意重复的键对应的值取最后的一个 ★运算 ${5/2?int} 显示2 cap_first : 首字母大写 capitalize: 所有单词首刺目大写 html : 转换为HTML格式 < replaced with replaced with > & replaced with & " replaced with " index_of : 显示元素所在的位置 "abcabc"?index_of("bc") 返回值为1(下标从0开始) Contains:判断是否存在字符 It contains "ice" 输出: It contains "ice" Replace :替换 split(“XX”):截取XX之后的字符 - ${x} 输出: - some - test - text starts_with :字符串由什么开始返回布尔型 trim :去掉空格 seq_index_of 数组中元素的位置 ${colors?seq_index_of("blue")} 输出: 2 Default : 设置变量的默认值 Exists:放在if句 如果没有….. Mouse found No mouse found Creating mouse... Mouse found No mouse found 输出 : No mouse found Creating mouse... Mouse found if_exists 放在一般语句 (${mouse?if_exists}) Creating mouse... (${mouse?if_exists}) 输出: () Creating mouse... (Jerry) 删除空白行和空格 ... 让此标记内的代码都执行<#escape 后的?参数 From: ${mailMessage.From} Subject: ${mailMessage.Subject} Message: ${mailMessage.htmlFormattedBody} ... 输出: From: ${mailMessage.From?html} Subject: ${mailMessage.Subject?html} Message: ${mailMessage.htmlFormattedBody} ... [A1]默认值 [A2]这里的abc叫做namespace chunk, is_date, last, root, j_string, contains, is_hash, long, float, ends_with, namespace, matches, time, values, seq_last_index_of, uncap_first, byte, substring, is_transform, web_safe, groups, seq_contains, is_macro, index_of, word_list, int, is_method, eval, parent, xml, number, capitalize, if_exists, rtf, node_type, double, is_directive, url, size, default, is_boolean, split, node_name, is_enumerable, seq_index_of, is_sequence, sort, is_node, sort_by, left_pad, cap_first, interpret, children, node_namespace, chop_linebreak, date, short, last_index_of, is_collection, ancestors, length, trim, datetime, is_string, reverse, c, keys, upper_case, js_string, has_content, right_pad, replace, is_hash_ex, new, is_number, is_indexable, lower_case, string, exists, html, first, starts_with ##############2222222222222222############### struts2.0 标签+ftl标签 FreeMarker中文API手册(完整) http://blog.csdn.net/junjun16818/article/details/6990068 三目: ${true?string('5','7')} ${line.class.simpleName} 你好 list里面是object数组 ${line[1]}:${line[0]} 取得list的长度: 0)> 截取字符串:${news.title.substring(0,14)}...${news.title?if_exists} 拆分字符数组 10)> 1.注释: 包含在 2.注意: 由于Freemarker会将>解释成FTL标记的结束字符,所以对于>和>=可以使用括号来避免这种情况,例如 y)> 3. 定义局部变量 4. 导入模板文件 指定名字空 my 5. if指令 <#if animals.python.price Pythons are cheaper than elephants today. Pythons are not cheaper than elephants today. 6. list指令以及列表序号 第${being_index+1}个 ${being.name}${being.price} Euros ${x} - ${user} 输出结果是: - Joe - Fred - Julia - Kate include指令 7.判断是否为空 ${userInfo.userName?if_exists} 男女 checked 统计条件保证金返款数量 按年统计按月统计按日统计默认按日统计 8.截取字符串 ${carInfo.carNum.substring(0,1)} 9.freemarker的replace功能 替换字符串 replace 线路标签:${lineInfo.lineLableDescribe?replace('#','、')} ${s?replace(‘ba’, ‘XY’ )} ${s?replace(‘ba’, ‘XY’ , ‘规则参数’)}将s里的所有的ba替换成xy 规则参数包含: i r m s c f 具体含义如下: · i: 大小写不区分. · f: 只替换第一个出现被替换字符串的字符串 · r: XY是正则表达式 · m: Multi-line mode for regular expressions. In multi-line mode the expressions ^ and $ match just after or just before, respectively, a line terminator or the end of the string. By default these expressions only match at the beginning and the end of the entire string. · s: Enables dotall mode for regular expressions (same as Perl singe-line mode). In dotall mode, the expression . matches any character, including a line terminator. By default this expression does not match line terminators. · c: Permits whitespace and comments in regular expressions. 10.三目运算 ${true?string('5','7')} 11.string格式化单个Interpolation,下面是一个例子: ${answer} ${answer?string} ${answer?string.number} ${answer?string.currency} ${answer?string.percent} 输出结果是: $42.00 $42.00 42 $42.00 4,200% 12.插入日期值:根据缺省格式(由#setting指令设置)将表达式结果转换成文本输出;可以使用内建函数string格式化单个Interpolation,下面是一个使用格式模式的例子: ${lastUpdated?string("yyyy-MM-dd HH:mm:ss zzzz")} ${lastUpdated?string("EEE, MMM d, ''yy")} ${lastUpdated?string("EEEE, MMMM dd, yyyy, hh:mm:ss a '('zzz')'")} 输出的结果类似下面的格式: 2003-04-08 21:24:44 Pacific Daylight Time Tue, Apr 8, '03 Tuesday, April 08, 2003, 09:24:44 PM (PDT) 13.插入布尔值:根据缺省格式(由#setting指令设置)将表达式结果转换成文本输出;可以使用内建函数string格式化单个Interpolation,下面是一个例子: ${foo?string("yes", "no")} 输出结果是: yes 车型: 三厢轿车 Freemarker 内置函数 数字、字符串、日期格式化 一、 Sequence的内置函数 1. sequence?first 返回sequence的第一个值。 2. sequence?last 返回sequence的最后一个值。 3. sequence?reverse 将sequence的现有顺序反转,即倒序排序 4. sequence?size 返回sequence的大小 5. sequence?sort 将sequence中的对象转化为字符串后顺序排序 6. sequence?sort_by(value) 按sequence中对象的属性value进行排序 二、 Hash的内置函数 1. hash?keys 返回hash里的所有key,返回结果为sequence 2. hash?values 返回hash里的所有value,返回结果为sequence 例如: ${key}=${user[key]} 三、 操作字符串函数 1. substring(start,end)从一个字符串中截取子串 start:截取子串开始的索引,start必须大于等于0,小于等于end end: 截取子串的长度,end必须大于等于0,小于等于字符串长度,如果省略该参数,默认为字符串长度。 例子: ${‘str’?substring(0)}à结果为str ${‘str’?substring(1)}à结果为tr ${‘str’?substring(2)}à结果为r ${‘str’?substring(3)}à结果为 ${‘str’?substring(0,0)}à结果为 ${‘str’?substring(0,1)}à结果为s ${‘str’?substring(0,2)}à结果为st ${‘str’?substring(0,3)}à结果为str 2. cap_first 将字符串中的第一个单词的首字母变为大写。 ${‘str’?cap_first}à结果为Str 3. uncap_first将字符串中的第一个单词的首字母变为小写。 ${‘Str’?cap_first}à结果为str 4. capitalize将字符串中的所有单词的首字母变为大写 ${‘str’? capitalize}à结果为STR 5. date,time,datetime将字符串转换为日期 例如: ${date1}à结果为2009-10-12 ${date2}à结果为9:28:20 ${date3}à结果为2009-10-12 9:28:20 注意:如果指定的字符串格式不正确将引发错误。 6. ends_with 判断某个字符串是否由某个子串结尾,返回布尔值。 ${“string”?ends_with(“ing”)?string} 返回结果为true 注意:布尔值必须转换为字符串才能输出 7. html 用于将字符串中的、&和“替换为对应得":& 8. index_of(substring,start)在字符串中查找某个子串,返回找到子串的第一个字符的索引,如果没有找到子串,则返回-1。 Start参数用于指定从字符串的那个索引处开始搜索,start为数字值。 如果start大于字符串长度,则start取值等于字符串长度,如果start小于0, 则start取值为0。 ${“string”?index_of(“in”) à结果为3 ${“string”?index_of(“ab”) à结果为-1 9.length返回字符串的长度 ${“string”?length}à结果为6 10. lower_case将字符串转为小写 ${“STRING”?lower_case}à结果为string 11.upper_case将字符串转为大写 ${“string”?upper_case}à结果为STRING 12. contains 判断字符中是否包含某个子串。返回布尔值 ${“string”?contains(“ing”)?string} à结果为true 注意:布尔值必须转换为字符串才能输出 13. number将字符串转换为数字 ${“111.11”?number}à结果为111.11 14.replace用于将字符串中的一部分从左到右替换为另外的字符串。 ${“strabg”?replace(“ab”,”in”)} à结果为string 15.split使用指定的分隔符将一个字符串拆分为一组字符串 ${s} 结果为: This is split 16. trim 删除字符串首尾空格 ${“ String ”?trim} à结果为String 四、 操作数字 1. c 用于将数字转换为字符串 ${123?c} à结果为123 2. string用于将数字转换为字符串 Freemarker中预订义了三种数字格式:number,currency(货币)和percent(百分比)其中number为默认的数字格式转换 例如: ${tempNum} ${tempNum?string.number}或${tempNum?string(“number”)} à结果为20 ${tempNum?string.currency}或${tempNum?string(“currency”)} à结果为¥20.00 ${tempNum?string. percent}或${tempNum?string(“percent”)} à结果为2,000% 五、 操作布尔值 string 用于将布尔值转换为字符串输出 true转为“true”,false转换为“false” foo?string(“yes”,”no”)如果布尔值是true,那么返回“yes”,否则返回no strb" align="center"> ${index} ${p.userName?if_exists} 司机乘客 单程往返 ${(p.ver)!""} 默认 匹配成功 ${p.createTime?string("yyyy-MM-dd HH:mm:ss")} if, else, elseif 语法: ... ... ... ... ... 备注:condition、condition2···必须为boolean 类型,、可有0或多个。 实例: x is 1 x is 2 x is 3 4> x is 4 x is not 1 nor 2 nor 3 nor 4 备注: 号 必须转义,否则出错。。转义请参考其他文档。 switch, case, default, break 语法 ... ... ... ... ... 备注:该指令官方不推荐使用了,可以用if, else, elseif 指令代替。 list, break 语法 ... 备注: sequence 为一个sequence 或者 collection 类型。item 为 循环的变量。该指令中包含有两个特殊的循环变量, item_index:该值为当前循环的值。 item_has_next:该值为一个boolean类型,表明该循环是否含有下一个(是否为循环到了最后一个) 实例: ${x_index + 1}. ${x}, 输出: 1. winter, 2. spring, 3. summer, 4. autumn 实例: ${i} 备注:当x 为一个数值序列时,可以使用该list 列出两个数值之的值。(适合于表格的序号填写) 实例: ${x} 备注:可以用 来终止该循环。 freemarker常见语法大全 FreeMarker的插值有如下两种类型:1,通用插值${expr};2,数字格式化插值:#{expr}或#{expr;format} ${book.name?if_exists } //用于判断如果存在,就输出这个值 ${book.name?default(‘xxx’)}//默认值xxx ${book.name!"xxx"}//默认值xxx ${book.date?string('yyyy-MM-dd')} //日期格式 ${book?string.number} 20 //三种不同的数字格式 ${book?string.currency}-- ${book?string.percent}— //声明变量,插入布尔值进行显示 ${foo?string("yes","no")} <等大小比较符号使用需要注意:(xml的原因),可以用于比较数字和日期 使用lt、lte、gt和gte来替代<、和>= 也可以使用括号y)> 内置函数: 调用区别于属性的访问,使用?代替. 常见的一些内置函数 对于字符串 html-对字符串进行HTML编码 cap_first-使字符串第一个字母大写 lower_case-将字符串转换成小写 trim-去掉字符串前后的空白字符 对于Sequences(序列) size-获得序列中元素的数目 对于数字 int-取得数字的整数部分(如-1.9?int的结果是-1) 对于集合,可以使用数组的方式,使用下标索引进行访问 逻辑判断: if................ ... ... ...... ... Boolean类型的空值判断 空值判断可以写成 //注意${}为变量的渲染显示,而为定义等操作符的定义 switch............ ... ... ... ... ... 快速定义int的集合 //注意不需要[] 3:循环读取集合: 注意/的使用 ${stu} 与jstl循环类似,也可以访问循环的状态 item_index:当前变量的索引值 item_has_next:是否存在下一个对象 其中item名称为as后的变量名,如stu 集合长度判断 判断=的时候,注意只要一个=符号,而不是== 宏/模板 初步了解: 使用更像一个闭包closure,可以定义后,在脚本中任意地方引用,并原地起作用 Hello Joe! 使用的方式为: //同xml可以简写成 宏的参数定义,类似js,在宏名后 带参数进行传递定义 ${person} 调用带参数时,注意使用类似XML的属性格式进行传递,不需要关心顺序问题 参数默认值定义,如果没有,就必须要求传递完整的参数列表 Hello ${person}! 使用xml的嵌套内容进行传递宏调用,关键标签 调用时: The bordered text 标签可以在宏中多次调用,也可以将多个宏组合进行嵌套 for循环的精简版: 宏的循环变量,配合嵌套标签进行参数传递, //这里的三个参数,将会传递到嵌套内容中 ${c}. ${halfc} Last! //这里的内容由macro中的进行参数的传递,传递的数量任意,当注意需要宏接受这些 上述还需要注意;的使用 参数的数量是可变的,并不要求全部都有,但是效果不同 在模板中定义变量 在模板中定义的变量有三种类型: plain变量:可以在模板的任何地方访问,包括使用include指令插入的模板,使用assign指令创建和替换。 局部变量:在宏定义体中有效,使用local指令创建和替换。 循环变量:只能存在于指令的嵌套内容,由指令(如list)自动创建;宏的参数是局部变量,而不是循环变量 //全局的plain变量 内部循环变量将会隐藏同名的外部循环变量 外部导入的使用,可以用于模块化,并且提供公用性 如:lib/my_lib.ftl文件 Copyright (C) ${date} Julia Smith. All rights reserved. Email: ${mail} lib/my_inc.ftl文件 ${my.mail} ${mail} 输出结果将不会出现冲突 对于库中的变量修改,使用in关键字 函数定义:区别于宏对象,带返回值 函数,有返回参数 stringA[M .. N] 取子字符串,类似substring(stringA, M, N) 导入其他页面元素 options包含两个属性 encoding=”GBK” 编码格式 parse=true 是否作为ftl语法解析,默认是true,false就是以文本方式引入.注意在ftl文件里布尔值都是直接赋值的如parse=true,而不是 parse=”true” hash与list的定义 ${c.a} List片段可以采用: products[10..19] or products[5..] 的格式进行定义,当只局限于数字 ${v} 对变量的缺省处理 product.color!"red" 用compress directive或者transform来处理输出。 ...:消除空白行。 ...将输出压缩为一行。都需要包裹所需文档 freemarker可用"["代替"<".在模板的文件开头加上[#ftl]. 数字输出的另外一种方式 #{c.a;m0} 区别于${},这个例子是用于输出数字的格式化,保留小数的位数,详细如下 数字格式化插值可采用#{expr;format}形式来格式化数字,其中format可以是: mX:小数部分最小X位 MX:小数部分最大X位 在定义字符串的时候,可以使用''或者"",对特殊字符,需要使用\进行转义 如果存在大量特殊字符,可以使用${r"..."}进行过滤 ${r"${foo}"} ${r"C:\foo\bar"} Map对象的key和value都是表达式,但是key必须是字符串 可以混合使用.和[""]访问 book.author["name"] //混合使用点语法和方括号语法 为了处理缺失变量,FreeMarker提供了两个运算符: 用于防止对象不存在而导致的异常 !:指定缺失变量的默认值 ??:判断某个变量是否存在,返回boolean值 noparse指令指定FreeMarker不处理该指定里包含的内容,该指令的语法格式如下: ... ${firstName?html} 使用html对字符进行格式化处理,对于<等的过滤 escape , noescape指令,对body内的内容实用统一的表达式 看如下的代码: First name:${firstName} Last name:${lastName} Maiden name:${maidenName} 上面的代码等同于: First name:${firstName?html} Last name:${lastName?html} Maiden name:${maidenName?html} 定义全局变量的方式 // 可以同时定义多个变量,也可以使用循环来给变量赋值 ${n} ${x} setting指令,用于动态设置freeMarker的运行环境: 该指令用于设置FreeMarker的运行环境,该指令的语法格式如下:,在这个格式中,name的取值范围包含如下几个: locale:该选项指定该模板所用的国家/语言选项 number_format:指定格式化输出数字的格式 boolean_format:指定两个布尔值的语法格式,默认值是true,false date_format,time_format,datetime_format:指定格式化输出日期的格式 time_zone:设置格式化输出日期时
定义数据结构如下: typedef struct tagMyNode { Mytype type; //元件类型 MySubtype Subtype; //元件子类型 tagMyNode* input1; //输入端1 tagMyNode* input2; //输入端1 tagMyNode* output1; //输出端1 UINT input1value; //输入端input1的值 UINT input2value; //输入端input2的值 UINT output1value; //输出端output1的值 int inputs; //当前已经有几个输入端有值 int number; //对于输入结点的序号 CPoint Orgpoint; //记录元件左上角位置 int width; //记录元件宽度 int height; //记录元件高度 }MyNode;  元件类型:元件类型Mytype type中Mytype是一个枚举类型定义如下: enum Mytype { Node, //结点 Gate, //门 }; 分为两种类型:Node结点和Gate门。  元件子类型:元件子类型MySubtype Subtype中MySubtype也是一个枚举类 型,定义如下: enum MySubtype { Input, //输入端 Output, //输出端 ANDGate, //与门 ORGate, //或门 NOTGate, //非门 NORGate, //或非门 NANDGate, //与非门 XORGate, //异或门 };  指针连接: tagMyNode* input1; tagMyNode* input2; tagMyNode* output1 是指向此结点的指针。由于元件之是要相互连接的,于是设置这几个指针用于元件之的连接。其中特殊情况有: 非门:由于非门只有一个输入端,所以非门不用tagMyNode* input2; 输入结点:输入结点只有一个链接端(这里称之为触点),采用tagMyNode* output1 输出结点:同输入结点,只有一个触点,采用tagMyNode* input1;  保存触点值:由于要进行仿真计算,所以还需保存各个触点的值: UINT input1value; UINT input2value; UINT output1value; 同指针连接,有3种特殊情况: 非门:不用UINT input2value; 输入结点:采用UINT output1value; 输出结点:采用UINT input1value;  进位标志:int inputs; 在进行仿真计算时,要用进位标志辅助计算。如与门只有在两个输入端都有值时,即inputs==2时,才能进位。  输入结点序号:int number; 每个输入结点都有不同的序号,从1开始递增。  元件位置和大小: CPoint Orgpoint; int width; int height; Orgpoint用于记录元件在视图中左上角的坐标 width用于记录元件宽度 height用于记录元件高度  电路图编辑模块 电路图编辑模块又分为两个子模块:鼠标放置元件模块,鼠标连接元件模块 首先在工具栏中可以选择这两种状态,如图4 图4 在按钮上单击可以切换状态。 定义一个枚举类型MyStatus来记录当前状态: enum MyStatus { NONE, //鼠标连接元件状态 ANDGATE, ORGATE, NOTGATE, NORGATE, NANDGATE, XORGATE, NODEINPUT, NODEOUTPUT }; MyStatus Status; 其中:NONE为鼠标连接状态,其他为鼠标放置状态。  鼠标放置元件模块 其算法如图5: 图5  DrawObject函数: 首先根据Status的状态,即六个门,两个端结点。共8种来调用DrawObject函数  引入准备好的八张位图(六个门,两个端) CBitmap MyBitMap; MyBitMap.LoadBitmap (nID);  将引入的位图拷贝入窗体窗户区 BITMAP bmpInfo; MyBitMap.GetBitmap (&bmpInfo); pOldBitmap=dc.SelectObject (&MyBitMap); ClientDC.BitBlt (point.x ,point.y,bmpInfo.bmWidth ,bmpInfo.bmHeight,&dc,0,0,SRCAND); dc.SelectObject (pOldBitmap);  用全局变量bmWidth和bmHeight来保存元件的宽度和高度 bmWidth=bmpInfo.bmWidth ; bmHeight=bmpInfo.bmHeight ;  CreateMyObject函数 函数声明为:CreateMyObject(Mytype type, MySubtype Subtype, CPoint point)  初始化元件 MyNode* pNode=new MyNode; pNode->type =type; pNode->Subtype =Subtype; pNode->input1 =0; pNode->input2 =0; pNode->output1 =0; pNode->output2 =0; pNode->Orgpoint =point; pNode->width =bmWidth; pNode->height =bmHeight; pNode->input1value =0; pNode->input2value =0; pNode->output1value =0; pNode->inputs =0;  如果创建的元件为输入结点,则要创建并画输入结点前的序号,这里 采用一个全局数组CArrayint,CPoint> numpoint来记录结点前序号。 if(Subtype==Input) { //当创建Input时加入点到numpoint数组中 numpoint.Add (CPoint(point.x-15,point.y)); pNode->number =numpoint.GetSize (); //创建时重绘序号 redrawnum(); } 而redrawnum()函数就是将所有输入结点前的序号重绘。  最后将元件加入到全局链表CList MyList中。 MyList.AddTail (pNode);  鼠标连接元件模块 鼠标连接元件模块分为三个过程模块:鼠标移动模块,鼠标按下模块,鼠标抬起模块。  鼠标移动模块 其算法如图6 图6 代码如下: void CMyView::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default //此时必然是非画图状态,所以status==NONE; if(Status==NONE) { //当前点在某个物件上吗? 并且 //当前点在该物件触点上吗? if(IsPointInObject(point) && IsPointInPut(point)) { //全局变量pNodeNow是在IsPointInObject()这个函数里面记录的 //circlepoint和put是在IsInInput1() IsInInput2() IsInOutput1() //这三个函数中记录的 //判断此时触点时否己连接非常重要 if(IsPutLinked()) { //如果此时触点己连接,则退出 return; } //此时鼠标移进触点 //当前是连接态吗? if(IsLink) { //连接态画图 LinkStatusDraw(point); } //开启画圆圈态 IsDrawCircle=TRUE; //画圆圈 DrawMyCircle(); } else//此时鼠标移出触点 { //如果此时已画圆圈,则要擦除圆圈 if(IsDrawCircle==TRUE) { EraserMyCircle(); //关闭画圆圈状态 IsDrawCircle=FALSE; //重绘连接线 moveoutredrawline(); //重绘圆圈所在的那个物件,因为擦除圆圈的时候可能擦除了部分物件 //------------------- redrawMyObject(pNodeNow); //如果此时是连接状态,连接态画图 } if(IsLink) { //连接态画图 LinkStatusDraw(point); } } } CView::OnMouseMove(nFlags, point); }  两个关键状态:可连接态IsDrawCircle和正在连接态IsLink  可连接态IsDrawCircle 当且仅当鼠标移动到某个元件上的某个尚未连接的触点上,才开启可连接态IsDrawCircle。之所以取名IsDrawCircle是因为此时会在鼠标停留的尚未连接的触点上画一个黑色小圆圈。 当鼠标移动离开触点,可连接态IsDrawCircle关闭。  正在连接态IsLink 当鼠标按下(见图5)并且此时可连接态IsDrawCircle开启(为TRUE)时正在连接态IsLink开启。  判断当前点是否在某个元件函数:IsPointInObject() 其算法如图7 图7  判断当前点是否在该元件触点上函数:IsPointInPut() 其算法如图8 图8 与门与其它5个门有所不同,与门只有一个输入端,所以要分开来判断 对于输入结点,则判断当前点是否在第一个输出端触点。 对于输出结点,则判断当前点是否在第一个输入端触点。 输入结点和输出结点的这样判断,一眼看上去似乎反了,但实际上有利于整个程序的编写。可以简单地这样分类:总共只有两种端,一种输入,一种输出。 这样,我们就可以将判断触点分为三个函数: IsInInput1() IsInInput2() IsInOutput1() 拿IsInInput1()来分析: centerpoint=GetCirclePoint(Input_1); if(IsInArea(point)) { //说明此时就在触点Input_1,用全局变量put记录下来 put=Input_1; //如果当前点在,则要保存触点中心点 circlepoint=centerpoint; return TRUE; } else { //如果移出触点,肯定不要再保存中心点 return FALSE; } 首先,调用函数GetCirclePoint()来取得当前触点的中心点。然后调用IsInArea(point)函数来判断当前点point是否在以当前触点中心点为中心的矩形区域内。如果是,则用一个全局枚举变量put来记录来前触点是两个输入端和一个输出端中哪一个。 我们看这个枚举类型: enum Myput { Input_1, Input_2, Output_1 }; 接下来用一个全局变量circlepoint来记录当前触点中心点。再返回真。 如果当前点不在以当前触点中心点为中心的矩形区域内,则返回假。这时千万不能记录当前触点中心点。这点不注意会出大错。  判断当前触点是否已连接函数:IsPutLinked() BOOL CMyView::IsPutLinked() { switch(put) { case Input_1: if(pNodeNow->input1 !=0) return TRUE; break; case Input_2: if(pNodeNow->input2 !=0) return TRUE; break; case Output_1: if(pNodeNow->output1 !=0) return TRUE; } return FALSE; } 这里根据全局变量put的类型和全局变量pNodeNow所指向的元件, 就可以判断当前元件的当前触点是否已连接。如果连接相应指针不为0。返回真,否则返回假。  连接态画图函数:LinkStatusDraw() void CMyView::LinkStatusDraw(CPoint point) { CClientDC clientDC(this); CPen whitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=clientDC.SelectObject (&whitepen); clientDC.MoveTo (startpoint); clientDC.LineTo (lastpoint); clientDC.SelectObject (pOldPen); CPen redpen(PS_DOT ,1,RGB(255,0,0)); pOldPen=clientDC.SelectObject (&redpen); clientDC.MoveTo (startpoint); clientDC.LineTo (point); clientDC.SelectObject (pOldPen); lastpoint=point; //重绘所有输入结点前的序号 redrawnum(); //重绘连接线 LinkLineRedraw(startpoint,point); //重绘物件 lineRedraw(startpoint,point); } 这里,startpoint是鼠标按下开始连接时起始元件触点中心点坐 标,lastpoint是上一次鼠标移动所停留的点。为了实在连接时鼠标移动 的动画效果,我们要先擦除上一次移动画的线(用白笔),然后再从startpoint到当前点point画线。移动时由于不信的擦除重画,可能将先前已画的元件,输入结点前的序号,和已经连接好的线擦除。于是我们需要重绘。 重绘所有输入结点前的序号 redrawnum(); void CMyView::redrawnum() { CClientDC dc(this); char buffer[20]; CPoint point; //重绘所有Input前的序号 for(int i=0;iint.GetSize ();i++) { //将数字转换成字符,存于buffer中 _itoa(i+1,buffer,10); point=numpoint.GetAt (i); dc.TextOut (point.x ,point.y ,buffer); } } 由于每创建一个输入结点,就要相应地记录一个序号。这个序号的位置点记录在numpoint这个数组中。数组声明如下: CArrayint,CPoint> numpoint; 而数组的下标加1就为序号。 所以每次重绘为了方便,将所有序号都重绘。 重绘连接线 void CMyView::LinkLineRedraw(CPoint startpoint, CPoint point) { //将起点startpoint到终点point扩充成一个矩形drawrect CRect drawrect(startpoint,point); //rect用于产生连接线最大矩形 CRect rect; //rectInter用于计算两个矩形的相交区域 CRect rectInter; //point1和point2用于产生连接线最大矩形 CPoint point1; CPoint point2; drawrect.NormalizeRect (); drawrect.InflateRect (1,1); //遍历MyPointList链表 POSITION pos=MyPointList.GetHeadPosition (); while(pos!=0) { //pPointArray用于指向点数组对象首址 CArrayint,CPoint>* pPointArray=MyPointList.GetNext (pos); point1=pPointArray->GetAt (0); switch(pPointArray->GetSize ()) { //分两种情况 :2个点和4,5个点的情况 case 2: //2个点时 point2=pPointArray->GetAt (1); break; default: //4,5个点时 point2=pPointArray->GetAt (3); } //用point1和point2设置矩形rect rect.left =point1.x ; rect.top =point1.y; rect.right =point2.x; rect.bottom =point2.y; rect.NormalizeRect (); rect.InflateRect (1,1); //如果两个矩形相交,则要重绘 if(rectInter.IntersectRect (&drawrect,&rect)) { DrawLinkLine(pPointArray); } } } 主要的算法思想是:将起点startpoint到当前点point扩充成一个矩形drawrect,然后遍历连接线链表,将每根连接线扩充成一个矩形rect,再判断这两个矩形是否相交,若相交,则需要重绘这根连接线。 连接线链表声明如下: CListint,CPoint>*,CArrayint,CPoint>*> MyPointList; 链表中每个结点是一个数组对象的地址,而这个数组中每个元素是一个点。这样一个数组就表示了一根连接线,而一个链表可以遍历所以连接线。  画提示连接的小圆圈函数:DrawMyCircle() void CMyView::DrawMyCircle() { //此时全局变量circlepoint记录了要画圆圈的 //而pNodeNow指向了当前的物件 //将物件坐标中的circlepoint转换成VIEW中的坐标 int x,y; x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y +circlepoint.y; CClientDC dc(this); //创建一个黑色的画刷 CBrush brush(RGB(0,0,0)); //创建指针pOldBrush用于保存原来的画刷 CBrush* pOldBrush; //将黑色的画刷选进设备装置DC,并用pOldBrush保存原来的画刷 pOldBrush=dc.SelectObject (&brush); //画一个圆圈,圆心是(x,y) //半径是4 dc.Ellipse (x-4,y-4,x+4,y+4); //将原来的画刷选回 dc.SelectObject (pOldBrush); } 由于全局变量circlepoint保存的是元件内部的相对坐标,需要将它 转换成视图中的坐标 x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y +circlepoint.y; 以上两句完成坐标的转换。 然后以(x,y)为圆心,4为半径,画一个黑色小圆圈 dc.Ellipse (x-4,y-4,x+4,y+4)  擦除小圆圈函数:EraserMyCircle() void CMyView::EraserMyCircle() { int x,y; x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y +circlepoint.y; CClientDC dc(this); CPen whitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=dc.SelectObject (&whitepen); dc.Ellipse (x-4,y-4,x+4,y+4); dc.SelectObject (pOldPen); } 与画小圆圈不同的是,擦除时要选择白色的笔和白色的画刷(默认) CPen whitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=dc.SelectObject (&whitepen); 以上3句选择白色的笔。  鼠标移开触点重绘连接线函数:moveoutredrawline() 为什么需要这个函数,原因是在鼠标称出触点后,此时要擦除刚才画 的小圆圈,而如果此时已经生成了连接线,则会擦除掉连接线的一小部分。于是需要这个函数。 void CMyView::moveoutredrawline() { int x,y; x=pNodeNow->Orgpoint .x +circlepoint.x; y=pNodeNow->Orgpoint .y +circlepoint.y; CPoint point1; CPoint point2; point1.x=x-4; point1.y=y-4; point2.x=x+4; point2.y=y+4; LinkLineRedraw(point1,point2); } 此时pNodeNow指向刚擦除小圆圈的元件,而circlepoint则记录着 触点中心。于是只要将以ciclepoint为中心的半径为4的矩形的左上角点和右下角点为参数调用LinkLineRedraw即可。  重绘元件函数redrawMyObject() void CMyView::redrawMyObject(MyNode* pNode) { switch(pNode->Subtype ) { case ANDGate: DrawObject(pNode->Orgpoint ,IDB_ANDGATE); break; case ORGate: DrawObject(pNode->Orgpoint,IDB_ORGATE); break; case NOTGate: DrawObject(pNode->Orgpoint,IDB_NOTGATE); break; case NORGate: DrawObject(pNode->Orgpoint,IDB_NORGATE); break; case NANDGate: DrawObject(pNode->Orgpoint,IDB_NANDGATE); break; case XORGate: DrawObject(pNode->Orgpoint,IDB_XORGATE); break; case Input: DrawObject(pNode->Orgpoint,IDB_NODEINPUT); break; case Output: DrawObject(pNode->Orgpoint,IDB_NODEOUTPUT); break; } } 该函数参数为指向元件的指针,用于重绘所指向的元件。  鼠标按下模块 如图5 图5 前面已经分析了放置元件状态,现在看连接元件状态中的判断: “当前点是否在某个元件未连接的触点上”其实就是判断“可连接态”IsDrawCircle是否为真。代码如下: if(IsDrawCircle)//当前点在某个元件未连接的触点上 { //全局变量IsLink表示开始连接状态 IsLink=TRUE; //全局变量pNodeStart记录当前物件 pNodeStart=pNodeNow; //全局变量startpoint记录当前触点中心坐标(注,此时要进行坐标转换 startpoint.x=pNodeNow->Orgpoint .x +circlepoint.x; startpoint.y=pNodeNow->Orgpoint .y +circlepoint.y; //全局变量startput记录当前触点类别:Input_1,Input_2,Output_1; startput=put; //lastpoint用于鼠标移动时擦除线效果 lastpoint=startpoint; } 进行连接初始化:首先开启开始连接状态 IsLink=TRUE; 然后用全局变量pNodeStart指向当前元件 pNodeStart=pNodeNow 全局变量startpoint记录当前触点中心坐标(这时要进行坐标的转换) startpoint.x=pNodeNow->Orgpoint .x +circlepoint.x; startpoint.y=pNodeNow->Orgpoint .y +circlepoint.y; 全局变量startput记录当前触点类别 startput=put; 最后lastpoint用于鼠标移动时擦除线效果 lastpoint=startpoint;  鼠标抬起模块 其算法如图9 图9 代码如下: void CMyView::OnLButtonUp(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default if(IsLink) { //首先擦除从startpoint到point CClientDC clientDC(this); CClientDC* pDC=&clientDC; CPen whitepen(PS_SOLID,1,RGB(255,255,255)); CPen* pOldPen; pOldPen=clientDC.SelectObject (&whitepen); clientDC.MoveTo (startpoint); clientDC.LineTo (point); clientDC.SelectObject (pOldPen); //重绘所有输入结点前的序号 redrawnum(); //重绘连接线 LinkLineRedraw(startpoint,point); //重绘物件 lineRedraw(startpoint,point); if(IsDrawCircle) { //用全局变量pNodeCurrent记录终点连接的物体 pNodeCurrent=pNodeNow; //用全局变量currentput记录终点连接的触点 currentput=put; //用全局变量currentpoint记录终点触点的中心坐标 currentpoint.x=pNodeNow->Orgpoint .x +circlepoint.x; currentpoint.y=pNodeNow->Orgpoint .y +circlepoint.y; //IsTwoObjectsCanLink()函数判断两个物件是否能连接 if(IsTwoObjectsCanLink()) { //先擦除圆圈 //EraserMyCircle();没有必要,只要鼠标移开时重绘连接线就可 //开始两个物件的画图连接 LineLink(); //开始真正连接:指针连接 RealLink(); } } //关闭连接状态: IsLink=FALSE; } CView::OnLButtonUp(nFlags, point); }  判断两个元件是否可以连接 BOOL CMyView::IsTwoObjectsCanLink() { //判断两个物件是否能连接 //这两个物件分别由pNodeStart和pNodeCurrent指向 //两个触点分别由startput和currentput标识 //若所指同一物件 if(pNodeStart==pNodeCurrent) { MessageBox("连接错误!自身物件不能相互连接"); return FALSE; } //输出直接结输出 if(startput==Output_1 && currentput==Output_1) { MessageBox("连接错误!输出端不能相互连接"); return FALSE; } //输入直接连接输入 if( (startput==Input_1 || startput==Input_2) && (currentput==Input_1||currentput==Input_2) ) { MessageBox("连接错误!输入端不能相互连接"); return FALSE; } //循环连接 if( (startput==Output_1) &&(currentput==Input_1||currentput==Input_2) ) { if(pNodeCurrent->output1 ==pNodeStart) { MessageBox("连接错误!不能循环连接"); return FALSE; } } if( (startput==Input_1||startput==Input_2) &&(currentput==Output_1) ) { if(pNodeStart->output1 ==pNodeCurrent) { MessageBox("连接错误!不能循环连接"); return FALSE; } } //如果以上情况都不发生,表示可以连接 return TRUE; } 用图来表示上述几种错误: 同一元件不能连接 图10 输出端不能连接输出端 图11 输入端不能连接输入端 图12 两个元件不能循环连接 图13  两个元件的画图连接:LineLink() 该函数调用了recordLine() 代码如下: void CMyView::recordLine () { //记录两个物件之的连接线经过的关键点 //先动态生成一个数组CArrayint,CPoint>之对象 //记录下连接线的关键点,然后将这个数组对象之地址加入到 //CListint,CPoint>*,CArrayint,CPoint>*> MyPointListint x0,y0,x1,y1,delta_x,delta_y; //(x0,y0)用于记录输出端起始点坐标 //(x1,y1)用于记录输入端终点坐标 //delta_x,delta_y用于记录x和y的偏移量 //一定是从输出端向输入端画线 if(startput==Output_1) { x0=startpoint.x; y0=startpoint.y; x1=currentpoint.x; y1=currentpoint.y; } else { x1=startpoint.x; y1=startpoint.y; x0=currentpoint.x; y0=currentpoint.y; } delta_x=5; //动态生成数组对象 CArrayint,CPoint>* pPointArray=new CArrayint,CPoint>; //根据点的位置分为三种情况:2个点,4个点,5个点 if(x0intArray->Add (CPoint(x0,y0)); pPointArray->Add (CPoint(x1,y1)); } else { //4个点情况 pPointArray->Add (CPoint(x0,y0)); pPointArray->Add (CPoint(x0+delta_x,y0)); pPointArray->Add (CPoint(x0+delta_x,y1)); pPointArray->Add (CPoint(x1,y1)); } } else if(x0==x1) { //两个点情况 pPointArray->Add (CPoint(x0,y0)); pPointArray->Add (CPoint(x1,y1)); } else //x0>x1 { //5个点情况 if(y0intArray->Add (CPoint(x0,y0)); pPointArray->Add (CPoint(x0,y0+delta_y)); pPointArray->Add (CPoint(x1-delta_x,y0+delta_y)); pPointArray->Add (CPoint(x1-delta_x,y1)); pPointArray->Add (CPoint(x1,y1)); } //加入当前数组对象地址到MyPointList MyPointList.AddTail (pPointArray); //用数组中的点画线 DrawLinkLine(pPointArray); } 首先保证从输出端向输入端画线,这样可以统一画线操作。 然后动态生成数组: CArrayint,CPoint>* pPointArray=new CArrayint,CPoint>; 用指针pPointArray指向该数组,用于存储连接线的关键点。 连接线根据位置总共有三种线型,如下图所示: (1)两个关键点的连接线: 图14 (2)4个关键点的连接线 图15 (3)5个关键点的连接线 图16  两个元件的指针连接:RealLink(); 其代码如下: void CMyView::RealLink() { //一定是输入连接输出 或 输出连接输入 if(startput==Input_1||startput==Input_2) { //输入连接输出 if(startput==Input_1) { pNodeStart->input1 =pNodeCurrent; } else { pNodeStart->input2 =pNodeCurrent; } pNodeCurrent->output1 =pNodeStart; } else//startput==Output_1 { //输出连接输入 pNodeStart->output1 =pNodeCurrent; if(currentput==Input_1) { pNodeCurrent->input1 =pNodeStart; } else { pNodeCurrent->input2 =pNodeStart; } } } 指针连接只有两种情况:输入连接输出和输出连接输入。可以用下图来表示 输入端连接输出端 图17 输出端连接输入端 图18  元件库模块 代码如下: UINT CMyView::gatefunction(MyNode *pNode) { UINT result; switch(pNode->Subtype ) { case ANDGate: result=pNode->input1value & pNode->input2value ; break; case ORGate: result=pNode->input1value | pNode->input2value ; break; case NOTGate: result=pNode->input1value ; result=1-result; break; case NORGate: result=pNode->input1value | pNode->input2value ; result=1-result; break; case NANDGate: result=pNode->input1value & pNode->input2value ; result=1-result; break; case XORGate: result=pNode->input1value ^ pNode->input2value ; } return result; } 这里pNode是指向当前元件的指针,根据当前元件的类型,及当前元件的输入端的值input1value和input2value(注:非门只有一个输入端)来返回元件的输出端的值。 各个门真值表如下表所示: (1)与门 输入端1 输入端2 输出端 0 0 0 0 1 0 1 0 0 1 1 0 表1 (2)或门 输入端1 输入端2 输出端 0 0 0 0 1 1 1 0 1 1 1 1 表2 (3)非门 输入端 输出端 0 1 1 0 表3 (4)与非门 输入端1 输入端2 输出端 0 0 1 0 1 1 1 0 1 1 1 0 表4 (5)或非门 输入端1 输入端2 输出端 0 0 0 0 1 1 1 0 1 1 1 1 表5 (6)异或门 输入端1 输入端2 输出端 0 0 0 0 1 1 1 0 1 1 1 0 表6  计算结果(仿真)模块 仿真模块在整个仿真器中占有最重要的作用。 当在视图窗体上放置元件,连接元件后。接下工具栏开始按钮 开始计算结果,进行仿真。 其主要算法如图19 图19 代码如下: void CMyView::OnBegin() { //开始计算,输出真值表 // TODO: Add your command handler code here //判断是否能够连接 if(CalculateResult()==-1) { MessageBox("连接线路失败!请检查线路"); } else { //可以连接 //调用函数计算 beginCalculate(); //生成对话框对象 CMyDialog MyDialog; MyDialog.DoModal (); } } 其中判断线路是否正确调用了CalculateResult(),这是仿真中最重要的函数。它的返回值是最终输出结点的值。如果返回-1,说明线路有误。其具体的算法如图20: CalculateResult() 图20 代码如下: int CMyView::CalculateResult() { //用于从输入端开始计算输出端结果 //遍历所有输入结点 MyNode* pNode; MyNode* pNodeNext; POSITION pos=MyList.GetHeadPosition (); while(pos!=0) { pNode=MyList.GetNext (pos); //判断当前结点是否是输入结点 if(pNode->Subtype ==Input) { for(;;) { //判断当前的输入结点的输出端指向的结点是否为空 //如果为空,表示连接失败 if(pNode->output1 ==0) { //连接失败,返回-1 return -1; } //否则不为空 //输出到它指向结点的输入端 //此时要判断输入到哪个输入端:input1 OR input2; pNodeNext=pNode->output1 ; if(pNodeNext->input1 ==pNode) { //如果是输入到input1 pNodeNext->input1value =pNode->output1value ; //输入的值++,如果到了2,就可以计算进位了 pNodeNext->inputs ++; } else { //如果是输入到input2 pNodeNext->input2value =pNode->output1value ; pNodeNext->inputs ++; } //指针跳向下一个结点 pNode=pNodeNext; //判断此时是否是输出结点,如果是返回输出结点的值input1value; if(pNode->Subtype ==Output) { return pNode->input1value ; } //判断是否可以进位,对于非门,只要有一个输入值即可inputs==1 //对于其他门,要两个输入值inputs==2 if(pNode->Subtype==NOTGate) { //非门 if(pNode->inputs==1) { //可以进位 pNode->output1value =gatefunction(pNode); } else { //不能进位 break;//跳出for(;;) } } else { //其他门 if(pNode->inputs ==2) { pNode->output1value =gatefunction(pNode); } else { //不能进位 break;//跳出for(;;) } } //请空输入值个数inputs,以便下次计算 pNode->inputs =0; }//for(;;) }//判断当前结点是否是输入结点 }//while(pos!=0) //遍历完后若没有返回,说明连接失败 return -1; } 其算法主要思想是:遍历每一个输入结点,将输入结点的值送入到它所连接的元件的输入端,若此时该元件可以进位,则调用该元件进位函数gatefunction()计算该元件的输出端的值,再将该输出端的值送入它的下一个连接元件,再判断下一个元件能否进位,如此循环,直到输出结点。若此时不可以进位,则遍历下一个输入结点。 可以用下图来说明: 图21 假设此时输入结点遍历的顺序是1->2->3 (顺序不唯一)。假设此时1,2,3号输入结点取值0,1,0。首先将1号输入结点的值送入它所连接的或门的第一个输入端,即input1value=0。此时或门进位标志inputs= =1。于是不能进位。遍历下一个输入结点2,将2号1号输入结点的值送入它所连接的与门的第一个输入端,同样此时与门进位标志也为inputs= =1,不能进位。最后遍历到输入结点3,将值送入到与门的输入端2。由于此时有两个输入了,即与门进位标志inputs= =2,调用与门函数计算与门输出端output1value.然后将此值送入或门,同样或门进位标志inputs= =2,调用或门函数计算或门输出端值,最后送入输出结点,结束。 计算真值表:beginCalculate() 代码如下: void CMyView::beginCalculate() { //计算输入结点的个数,输出真值表 n=numpoint.GetSize (); //计算要进行循环的次数 int i; x=1; for(i=1;i<=n;i++) { x=x*2; } //动态生成x个字符串保存真值表 //用一个字符串格式化数据 CString str; //用一个数组I[1]~I[n]记录每个输入结点值 UINT* I=new UINT[n+1]; //用数组J[1]~J[n]辅助计算 UINT* J=new UINT[n+1]; //初始化J[1]~J[n] J[1]=1; for(i=2;i<=n;i++) { J[i]=J[i-1]*2; } //进行x次循环,计算真值表 for(i=0;istrs.Add (CString()); for(int k=1;k<=n;k++) { I[k]=i & J[k]; //向右移位 I[k]=I[k]>>(k-1); //将输入端1~n的值加入字符串 str.Format ("%d ",I[k]); // //连接起来 strs[i]=strs[i]+str; } //给输入结点1~n初始化值 POSITION pos=MyList.GetHeadPosition (); MyNode* pNode; while(pos!=0) { pNode=MyList.GetNext (pos); //如果结点是输入结点 if(pNode->Subtype ==Input) { //结点中的pNode->number 记录了结点序号 //给结点初始化值 pNode->output1value =I[pNode->number ]; } } //调用函数计算,将计算结果保存 int result=CalculateResult(); //生成字符串以便输出 str.Format ("%10d",result); strs[i]+=str; } } 代码分析: (1) 首先得到输入结点的个数:n=numpoint.GetSize (); 这里numpoint是记录输入结点前序号位置点的数组,而有多少个这样的点,就有多少个输入结点。 (2)然后计算真值表的行数,因为有n个输入结点,真值表就有2^n行。 x=1; for(i=1;i<=n;i++) { x=x*2; } 这里我们用x来保存真值表的行数。 (3)产生所有的输入结点值的组合。 如果有3个输入结点,它的所有组合如下表 输入3 输入2 输入1 i 0 0 0 0 0 0 1 1 0 1 0 2 0 1 1 3 1 0 0 4 1 0 1 5 1 1 0 6 1 1 1 7 我们可以从表的第四列看出可以用数字i从0到x-1来分解出这些组合。 例如:当i为2时,它在内存中最后3位为:010。 此时 输入3=010 & 100=000,再右移2位即可得到0。 于是我们可以得到:输入1 & 001 再右移0位 输入2 & 010 再右移1位 输入1 & 100 再右移2位 我们用一个数组J[1]~J[n]来记录相与的数字。为1,2,4,......,2^(n-1) 初始化J[1]~J[n] J[1]=1; for(i=2;i<=n;i++) { J[i]=J[i-1]*2; } 用I[1]~I[n]记录输入1~输入n 产生一行输入值: strs.Add (CString()); for(int k=1;k<=n;k++) { I[k]=i & J[k]; //向右移位 I[k]=I[k]>>(k-1); //将输入端1~n的值加入字符串 str.Format ("%d ",I[k]); // //连接起来 strs[i]=strs[i]+str; } 给输入结点1~n初始化值 POSITION pos=MyList.GetHeadPosition (); MyNode* pNode; while(pos!=0) { pNode=MyList.GetNext (pos); //如果结点是输入结点 if(pNode->Subtype ==Input) { //结点中的pNode->number 记录了结点序号 //给结点初始化值 pNode->output1value =I[pNode->number ]; } } 调用函数计算,将计算结果保存 int result=CalculateResult(); 最后生成字符串以便输出 str.Format ("%10d",result); strs[i]+=str; 完成以上操作后,生成一个对话框,然后将字符串数组strs[]加入到列表框内。最终输出整个真值表。 参考文献: 1 Electronics Workbench 5.0 1992-1996 Interactive Image Technologies Ltd 2 MSDN Libary July 2000 Microsoft

16,551

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Creator Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧