C++ 和 MFC的学习
1. 在Windows应用程序设计中,既可以使用个C的基本数据类型,也可以使用Windows自定义的数据类型。但要注意,凡是Windows自定义的关键字都要大写。
2. 什么是句柄?
在Windows应用程序设计中,存在着很多诸如窗口、按钮、滚动条等复杂的程序对象和实例。为了在程序中区分和使用它们,必须对它们进行标识。为了区别于普通变量的标识,Windows把这种复杂对象的标识叫做“句柄”。不同Windows对象的句柄具有不同的类型。
3. 事件与消息
Windows应用程序的另一个突出特点是在程序启动并创建了程序窗口之后,随即就进入了一个等待状态(利用一个while循环),知道接收到了某种刺激(例如:键盘输入,鼠标的单机或双击)之后,程序才会脱离等待状态对这个刺激进行处理,而处理完毕后又进入等待状态。这些有可能触发计算机程序作出相应反应的刺激,叫做“事件”。
为了描述事件的各种信息(例如,何地、何时发生了何种时间等),Windows定义了一个结构,这个结构就叫做“消息(message)”。
消息结构体定义如下:
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG,*LPMSG,*PMSG;
4. 消息循环
系统为每个应用程序都建立了一个叫做消息队列的存储空间,在程序运行的过程中如果发生了一个事件,Windows就会把这个事件所对应的消息送入消息队列等待使用。应用程序可以通过调用Windows提供的API函数GetMessage()从消息队列中获取消息。并利用GetMessage()函数的返回值(当获得消息时,该函数返回TRUE,否则返回FALSE)组织一个循环,从而可以不断从消息队列获取消息,一旦获取了一个消息,酒吧这个消息发送给系统(注意,不是把消息发送给应用程序,而是发送给系统!)。这个循环叫做消息循环,在Windows应用程序中,消息循环的代码如下:
while(GetMessage(&msg,NULL,NULL,HULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
5. ::在c++中是表示全局变量或函数,如果当前方法块内部没有同名的局部变量或函数可以不要;
在用MFC写windows程序时,除了上述作用,还有表示系统api的作用;<br>mfc对api进行了封装,api变成了不同类的内部函数,用::表示调用纯的api。
6. 有默认参数的函数,函数在声明时,默认形参已经被赋值,如果主调函数传递实参,那么就使用传递过来的数据,如果主调函数没有传递该实参,那么使用默认的参数。
如果函数的定义在函数调用之前,则应在函数定义中给出默认值。如果函数的定义在函数调用之后,则在函数调用之前需要有函数声明,此时必须在函数声明中给出默认值,在函数定义时可以不给出默认值。也就是说在函数调用之前将默认值的信息通知编译系统。如下代码:
代码一:
#include <iostream>
using namespace std;
void Display(int a=111,int b=222);
int main(void)
{
Display(3);
return 0;
}
void Display(int a,int b)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
}
代码二:
#include <iostream>
using namespace std;
void Display(int a=111,int b=222)
{
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
}
int main(void)
{
Display(3);
return 0;
}
7. 局部变量可以和全局变量同名,这样在局部变量作用域内,对该变量的使用,使用的是局部变量,在局部变量作用域之外,对该变量的使用,使用的是全局变量。即:如果在同一个源文件中,全局变量与局部变量同名,则在局部变量的作用范围内,全局变量被屏蔽,即它不起作用。如下例子的运行结果:
#include <iostream> using namespace std; int a = 888; int main(void) { { int a = 12; cout<<"a = "<<a<<endl; } cout<<"a = "<<a<<endl; return 0; }
gcc运行结果为:
a = 12
a = 88
8. 静态存储方式和动态存储方式:所谓静态存储方式是指在程序运行期间,系统对变量分配固定的存储空间。而动态存储方式则是在程序运行期间,系统对变量动态地分配存储空间。
先看一下内存中供用户使用的存储空间的情况。这个存储空间可以分为三部分,即:
(1)程序区
(2)静态存储区
(3)动态存储区
如下图示:
程序区 |
静态存储区 |
动态存储区 |
数据分别存放在静态存储区和动态存储区中。全局变量全部存放着静态存储区中,在程序开始执行时给全局变量分配存储单元,程序执行完毕就释放这些空间。在程序执行过程中它们占据固定的存储单元,而不是动态地进行分配和释放。
在动态存储区中存放一下数据:①函数形式参数。在调用函数时给形参分配存储空间。②函数中的自动变量(为加static声明的局部变量)。③函数调用时的现场保护和返回地址等。
9. 静态局部变量static与自动变量auto:
静态变量static:有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量保留上一次函数调用结束时的值。这是就应该指定该局部变量为静态局部变量。
如下代码:
#include <iostream>
using namespace std;
void OutputData(void)
{
static int a=5;
a++;
cout<<"a = "<<a<<endl;
}
int main(void)
{
OutputData();
OutputData();
return 0;
}
在gcc下执行结果为:
a = 6
a = 7
注意:①为静态局部变量赋初值是在编译时进行的,即只赋初值一次,在程序运行时它已有初值。以后每次调用函数时不再重新赋初值,而只是保留上次函数调用结束时的值。而为自动变量赋初值,不是在编译时进行的,而是在函数调用时进行,每调用一次函数重新给一次初值,相当于执行一次赋值语句。
②如果在定义局部变量时不赋初值的话,对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符型变量)。而对自动变量来说,如果不赋初值,则它的值是一个不确定的值。
③虽然静态局部变量在函数调用结束后仍然存在,但其他函数是不能引用它的,也就是说,在其他函数中它是“不可见”的。
自动变量:函数中的局部变量,如果不用关键字static加以声明,编译系统对它们是动态地分配内存空间的。在调用该函数时,系统给形参和函数中定义的变量分配存储空间,数据存储在动态存储区中。在函数调用结束时就自动释放这些空间。因此这类局部变量成为自动变量。自动变量用关键字auto做存储类别的声明。
如下代码:
#include <iostream>
using namespace std;
void OutputData(void)
{
int a=5;
a++;
cout<<"a = "<<a<<endl;
}
int main(void)
{
OutputData();
OutputData();
return 0;
}
gcc下的执行结果为:
a = 6
a = 6
10. 在多文件的程序中声明外部变量:
一个C++程序可以由一个或多个源程序文件组成。如果一个程序包含两个文件,在两个文件中都要用到同一个外部变量num,不能分别在两个文件中各自定义一个外部变量num,否则在进行程序连接时会出现“重复定义”的错误。正确的做法是:在任一个文件中定义外部变量num,而在另一个文件中用extern对num作外部变量声明:即
extern num;
编译系统由此知道num是一个已在别处定义的外部变量,它现在本文件中找有无外部变量num,如果有,则将其作用域扩展到本行开始,如果本文件中无此外部变量,则在程序进行连接时从其他文件中找有无外部变量num,如果有,则把另一文件中定义的外部变量num的作用域扩展到本文件,在本文件中可以合法地引用该外部变量num。
11. 静态外部变量:有时在程序设计中希望某些外部变量只限于被本文件引用,而不能被其他文件引用。这时可以在定义外部变量时加一个static声明。这种加上static声明只能用于本文件的外部变量(全局变量)称为静态外部变量。
在程序设计中,常有若干人分别完成各个模块,各人可以独立地在其设计的文件中使用相同的全局变量而互不相干。只需在每个文件中的全局变量前加上static即可。这就为程序的模块化、通用性提供了方便。如果已知道其他文件不需要引用本文件的全局变量,可以对本文件中的全局变量都加上static,成为静态外部变量,以免被其他文件误用。
需要指出,不要误认为用static声明的外部变量才采用静态存储方式(存放在静态存储区中),而不加static的是动态存储(存放在动态存储区)。实际上,两种形式的外部变量都用静态存储方式,只是作用范围不同而已,都是在编译时分配内存的。
static声明使变量采用静态存储方式,但它对局部变量和全局变量所起的作用不同。对局部变量来说,static使变量由动态存储方式改变为静态存储方式。而对全局变量来说,它使变量局部化(局部于本文件),但仍为静态存储方式。从作用域角度看,凡是有static声明的,其作用域都是局限的,或者局限于本函数内(静态局部变量),或者局限于本文件中(静态外部变量)。
12. 内部函数和外部函数:如果一个函数只能被本文件中其他函数所调用,它称为内部函数。在定义内部函数时,在函数名和函数类型的前面加static。
在定义函数时,如果在函数首部的最左端冠以关键字extern,则表示此函数是外部函数,可供其他文件调用。
注意:如果在定义函数时省略extern,则默认为外部函数。这就是为什么我们一般写外部函数,在.h文件中的函数声明前并没有加extern的原因。
书写习惯:经考究stm32库函数写法,内部函数的声明放在源文件(.cpp/.c)中,声明和定义都需要在前面加上关键字static。外部函数的声明放在头文件(.h)中,声明和定义钱都要加关键字extern,也可省略extern,默认不加extern时就是外部函数。这样当头文件被其他源文件包含时,该文件就可使用外部变量。
13. 宏定义:用#define进行常量定义。
注意点:宏定义中,如果后面的表达式含有运算,一定要加括号,否则将很有可能出错,如下代码:
#include <iostream>
using namespace std;
#define FUCK_NUM 1+2*3
int main(void)
{
int a =2,b=2;
a = a*FUCK_NUM;
b = FUCK_NUM*b;
cout<<"a = "<<a<<endl;
cout<<"b = "<<b<<endl;
return 0;
}
其运算结果为:
a = 8
b = 13
还可以用#define命令定义带参数的宏定义。其定义的一般形式为:
#define 宏名(参数列表) 字符串
例如常用的:
#define BIT(X) (1<<X)
以及:
#include <iostream>
using namespace std;
#define TOTAL(A,B,C,D) (A+B+C+D)
int main(void)
{
cout<<TOTAL(1,2,3,4)<<endl;
return 0;
}
执行结果为:10
14. 文件包含(#include):其作用仅是文件内容的拷贝,将被包含文件中的所有文本拷贝到包含文件中指定的位置。并无其他高级操作。
注意:#include “文件名” 和 #include <文件名>的区别。
#include “文件名” :现在用户目录下查找该文件,如果找不到,再到系统目录下查找该文件。
#include <文件名> :直接到系统目录下查找该文件。