这次又到了比较简单的内容了,介绍了一些基本的循环控制、关系表达式、逻辑控制等。
这里就简单抽一些稍微比较特别的内容来吹吹水,不,是来总结一下吧~
一、i++和++i
不,我不是来解释i++和++i的区别的,只是书上提到一点,在使用for循环的时候,倒底i++和++i有没有区别呢?
答案是:几乎没有,随便你用哪一个。
但是,要稍微注意一下两种方式的内部处理方式:
i++:先复制一个i的副本,然后将i加1,最后返回i的副本。
++i:将i加1,然后返回i。
这么一说,大家都知道谁的效率更高,但对于现在的编译器而言,是会自动优化的(但不知道是不是所有编译器都这样)。
然后,C++是允许自定义操作符的行为的,也就是说,我们可以为自己的类定义++操作,这时候,复制类的副本,消耗明显就不能忽略不计了。
这个,就不多说了。
二、逗号运算符
逗号运算符可以将多条语句放到一起,如:
for (int i = 2, j = 10 * i; i < 999; i++) {
cout << j;
break;
}
int i = 2, j = 10 * i;就是逗号表达式了,那么,j的值是多少呢?没错,是20。
再来, 这样:
int num = 0;
num = 10, 50;
注意,声明变量赋值的时候是不能使用逗号表达式的,这个自己在编译器编译一下就知道了~规则这东西,没必要死记~
这里的num值是多少呢?没错,是10,因为赋值比逗号表达式优先级要高,所以先把10赋值给num,后面的50被舍弃了。
那么,这样呢:num = (10, 50); 结果是50,因为括号的优先级比赋值语句高,逗号表达式沾了光,所以最后是把50赋值给了num。
但是,如果有谁非特殊情况下用这样的赋值形式,那么,我保证不打死他。
三、类型别名(typedef)
这又是新手的一个痛点,大部分初次接触Cocos2d-x的朋友,一定被这两条语句吓死过:
typedef void (Ref::*SEL_SCHEDULE)(float);
#define schedule_selector(_SELECTOR) static_cast<cocos2d::SEL_SCHEDULE>(&_SELECTOR)
这看似神复杂的东西,倒底是什么?这其实就是我们使用schedule定时器功能时要用到的。
这里先放着。
我们先来看看最简单的typedef用法:
typedef int IQ;
IQ mutouIQ = 250;
int normalIQ = 1;
所谓类型别名,就是给某个类型的名字取个外号,比如我们把使用IQ代替int,如上代码。
使用typedef int IQ,就代表把int取了个别名,叫做IQ,以后就可以用IQ来定义整数了。
粗俗地说,IQ就是int,int就是IQ,懂了吧~
再来看看这个:typedef char* mpointer;
那么,我们以后就可以使用mpointer来定义char类型的指针。
大家有没有发现什么?
如果把typedef去掉,就变成这样:
int IQ;
char* mpointer;
没错,IQ和mpointer就像是一个变量名(在上面这段代码里它们就是变量名)。
结果就是,在声明变量之后,前面加上typedef,就然后这个变量成为了类型的别名。
很好理解吧?以后看到typedef很复杂的,就把typedef去掉,看看是声明的什么变量,那这个别名就是代表这样的变量了。
接下来,再回到那神复杂的两条语句:
typedef void (Ref::*SEL_SCHEDULE)(float);
#define schedule_selector(_SELECTOR) static_cast<cocos2d::SEL_SCHEDULE>(&_SELECTOR)
先看第一条,很复杂,我们把typedef去掉,变成:void (Ref::*SEL_SCHEDULE)(float);
这是什么,其实我也解释不了,我C++很水的,但,这明显就是一个“函数变量”(暂时这么称呼它)。
一个返回值为void,参数为float的函数,并且限定其类为Ref,怎么样,很好理解吧。
把typedef放回去,这句代码就是把SEL_SCHEDULE定义为上述所说的那种函数。
接着看第二条代码,这是一个宏:
1.schedule_selector是宏的名称
2._SELECTOR是宏的参数
3.static_cast<cocos2d::SEL_SCHEDULE>(&_SELECTOR)简化为static_cast<SEL_SCHEDULE>(&_SELECTOR),这是强制类型转换,将传递进来的_SELECTOR转换为某种类型
4.转换为什么类型呢?没错,就是SEL_SCHEDULE类型,SEL_SCHEDULE是什么类型?刚刚已经解释过了,一个返回值为void,参数为float,其类为Ref的函数
最后,看看我们平时是怎么使用schedule_selector的:
this->schedule(schedule_selector(HelloWorld::update));
结果就是把我们的HelloWorld的update函数转换成了SEL_SCHEDULE类型,然后传递给schedule函数。
呼,这回明白了吧,最后看看我们的update函数是如何定义的:void update(float dt);
返回值为void,参数为float,HelloWorld类继承了Layer(最终也是继承了Ref的)。
update函数的类型和SEL_SCHEDULE的基本上一样,除了类的类型以外。
但因为Layer的最终基类是Ref,所以可以把update函数强制转换为SEL_SCHEDULE类型。
好了,我再解释下去的话,就越来越说不通了,就到这里吧~
四、新的for循环(c++11)
C++11新增一种for循环语法,可以很方便地遍历数组、vector以及array。
使用很简单,如下:
int nums[3] = { 1, 2, 3 };
for (int num : nums) {
}
这样就能把nums的值以此赋值给num,从而遍历整个数组,不多说喇~
五、cctype标准库
我问大家一个问题,给你一个字母A,要判断它是大写还小写,你怎么判断?
(小若:你都说了是字母A了,当然是大写的啊!要判断吗?)
咳咳~!程序!我们是程序员,不能这么简单地去判断~!这样不严谨~!
我们应该这样:
char c = 'A';
if(c >= 'A' and c <= 'Z') {
// 嗯,是大写
}
一般人应该都会这么想吧?(小若:不,一般人都知道A是大写,不用判断)
其实这样不安全,万一字符编码不是ASCII呢?有可能答案就不一样了。
于是,我们应该这样写:
char c = 'A';
if (isupper(c)) {
// 嗯,是大写
cout << "yes";
}
isupper是cctype库的函数,用来判断字母是否是大写。
这样就更方便和通用了,
cctype库在头文件ctype.h里(或者cctype里),关于这个库的更多函数,百度一下,会有很多~
六、结束
好了,这次就到这里吧,C++果然不是我喜欢的东西,能吹水的地方比较少(都是比较严谨的东西),我还能坚持多久呢?~