项目之MFC/VC++疑问巩固1

2019年4月29日22:20:24

1.#define

参考:#define_百度百科
在C或C++语言源程序中允许用一个标识符来表示一个字符串,称为"宏"。字符串取代宏名。简单代换,字符串中可以含任何字符,可以是常数,也可以是表达式,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。
宏定义不是说明或语句,在行末不必加分号,如加上分号则连分号也一起置换

一般形式为:
#define 标识符 字符串

带参宏定义的一般形式为:
#define 宏名(形参表) 字符串

#define 条件编译

#define PI 3.1415926
#define MAX(a,b) (a>b)?a:b
在用#define 定义时 ,可以用斜杠("") 续行.与vb中的下划线(" _")作用同.
比如:
#define add1( x, y ) ( x + y)
也可以表示成 :
#define add1(x,y)
(x + y )
-------------------------------------------------------------------------------------
const ~ # define比较
条款1:尽量用const和inline而不用#define http://www.kuqin.com/effectivec2e/ch00b.htm
在c++中从来就是建议能用const的地方不用define
define在编译期间不做类型检查,只进行字符替换,并且在字符替换时可能会产生意料不到的错误。
#define优点: 可以配合#ifdef等其它的宏和编译器的参数(如gcc的-D)方便地控制版本,控制哪部分代码只对调试有用,发布时自动把它去掉,而不用对高度好的代码做任何修改。这是const不具备的。
#define可以定义函数,并且自动就是内联函数,运行时没有函数调用的开销,速度快。
  在可移植性代码中,#define用的非常多。它能根据当前环境决定哪部分代码用于这个平台,不适用于这个平台的代码可以由它控制而不让编译。
  在代码需要不同的编译器分别编译时,由于用#define可以避免各编译器因支持的语法差异而带来的错误。如:有些编译器不支持inline关键字,那么#define可以通过设置一个变量来控制对inline的处理。其它比如try catch等,为防止有些编译器不支持,标准库就是这么控制的。
  在动态链接库方面,C和C++处理方式不同,为了能够使C/C++混合编程成为可能,库文件中也是通过#define控制的。比如:#ifdef __cplusplus。。。。表明下面的代码用C++编译器处理,否则用C编译器去处理。

1. typedef

参考:typedef_百度百科
typedef目的一般有两个,一个是给变量一个易记且意义明确的新名字,另一个是简化一些比较复杂的类型声明
---eg:
typedef int size;// 为现有类型int创建别名
size array[4];
---eg:
typedef char Line[81];// Line类型即代表了具有81个元素的字符数组
Line text,line;
---eg:
typedef char* pstr;// 隐藏指针语法
int mystrcmp(const pstr p1,const pstr p3);
---eg:
typedef struct tagNode
{
  char* pItem;
  struct tagNode* pNext;
}*pNode;
---eg:
struct tagMyStruct
{
  int iNum;
  long lLength;
};
可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。
typedef为这个新的结构起了一个名字,叫MyStruct:typedef struct tagMyStruct MyStruct;
typedef 与 #define的问题
typedef char* pStr1;//有分号
#define pStr2 char* //无分号
pStr1 s1,s2;
pStr2 s3,s4;
在上述的变量定义中,s1、s2、s3都被定义为char *,而s4则定义成了char,不是我们所预期的指针变量,根本原因就在于#define只是简单的字符串替换而typedef则是为一个类型起新名字
在许多C语言编程规范中提到使用#define定义时,如果定义中包含表达式,必须使用括号,如:
#define f(x) ((x)*(x))
1) #define宏定义有一个特别的长处:可以使用 #ifdef ,#ifndef等来进行逻辑判断,还可以使用#undef来取消定义。
2) typedef也有一个特别的长处:它符合范围规则,使用typedef定义的变量类型其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义则没有这种特性。

2. 条件编译

选择性编译,为了让程序在各种不同的软硬件环境下都以运行。提高程序的可移植性和灵活性。同一段代码,针对不同的环境,预编译成不同代码,从而使得生成的程序最大程度上适应用户的软硬件环境。
参考:条件编译_百度百科
…………
if格式
#if 表达式

[#else
]
#endif
功能:当表达式的值为真时,编译①,否则编译②。其中,#else和②可有可无。
…………
ifdef格式
#ifdef 标识符

[#else
]
#endif
功能:当标识符已被定义时(用#define定义),编译①,否则编译②。其中#else和②可有可无。
…………
ifndef格式
#ifndef 标识符

[#else
]
#endif
功能:当标识符未被定义时(用#define定义),编译①,否则编译②。

3. #ifdef __cplusplus

参考:#ifdef __cplusplus 倒底是什么意思?
#ifdef __cplusplus
extern "C" {
  #endif
  //一段代码
  #ifdef __cplusplus
}
#endif
__cplusplus是cpp中的自定义宏:如果这是一段cpp的代码,那么加入extern "C"{和}处理其中的代码。
C和C++对函数的处理方式是不同的.extern "C"是使C++能够调用C写作的库文件的一个手段,如果要对编译器提示使用C的方式来处理函数的话,那么就要使用extern "C"来说明。

4. __declspec(dllexport)和__declspec(dllimport)

参考:__declspec(dllimport)的作用__declspec(dllimport) Windows C++中__declspec(dllexport)的使用
动态链接库(DLL,Dynamic Link Library)。dllimport是为了更好的处理类中的静态成员变量的。
__declspec是Microsoft VC中专用的关键字,它配合着一些属性可以对标准C/C++进行扩充。__declspec关键字应该出现在声明的前面。__declspec(dllexport)用于Windows中的动态库中,声明导出函数、类、对象等供外面调用,省略给出.def文件。即将函数、类等声明为导出函数,供其它程序调用,作为动态库的对外接口函数、类等
.def文件(模块定义文件)是包含一个或多个描述各种DLL属性的Module语句的文本文件。.def文件或__declspec(dllexport)都是将公共符号导入到应用程序或从DLL导出函数。如果不提供__declspec(dllexport)导出DLL函数,则DLL需要提供.def文件。

2019年5月1日11:00:19

5. 标准的预处理器宏

已遇/常用:
* __cplusplus 使用 C++ 编译器编译
* __DATE__ 编译时的日期
* __FILE__ 编译文件名
* __func__ 同 __FUNCTION__
* __LINE__ 当前行号
* __TIME__ 编译时系统时间
参考:C++预处理_百度百科
GNU编译器:gcc中的预编译宏

6. 汇编

参考:汇编指令_百度百科
汇编指令速查
一点一点学汇编
一点一点学汇编2
机器语言:0,1代码→→汇编语言:助记符→→高级语言:如C,C++,java

7. 计算机硬件

参考:计算机基础系列一:计算机硬件
计算机硬件基础知识_图文
中央处理器(CPU,Central Processing Unit)

CPU+输入输出+主存储器(内存)构成了电子计算机的三大核心组件:
CPU是人的大脑,负责控制全身和运算;内存(主存储器)是人的记忆,负责临时存储;硬盘是人的笔记本,负责永久存储;输入设备是耳朵或眼睛或嘴巴,负责接收外部的信息存入内存;输出设备是你的脸部(表情)或者屁股,负责经过处理后输出的结果
…………
RAM(Random Access Memory):也叫主存,内存条,计算机内存性能主要取决于它。特点是其中存放的内容可随时供CPU读写,但断电后,存放的内容就会全部丢失。
ROM(Read-Only Memory):一种只能读出不能写入的存储器断电后其中的内容不会丢失。通常用于存放固定的、执行特殊任务的程序。目前常用的是可擦除、可编程的只读存储器。
…………
参考:寄存器与存储器物理区别
速度:寄存器(register) > 内存 > 硬盘
寄存器与存储器物理区别:
寄存器存在于CPU中,速度很快,但是所占面积大,数目有限;
存储器就是内存,速度稍慢,所占面积小,但数量很大;
计算机做运算时,必须将数据读入寄存器才能运算。

8. __asm _emit

__asm__和__volatile 在Visual C++中使用内联汇编(_emit) emit指令分析

2019年5月2日08:42:44

9. #pragma

参考:#pragma_百度百科
格式一般为: #Pragma Para
常用参数列表:
#Pragma message("消息文本")
  当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
#pragma once
  只要在头文件.h的最开始加入这条指令就能够保证头文件被编译一次。
#pragma hdrstop
  表示预编译头文件到此为止,后面的头文件不进行预编译。类似于条件编译。
#pragma warning( disable : 4000 40; once : 3000; error : 2000 )
  #pragma warning(disable:4000 40) // 不显示4507和34号警告信息
  #pragma warning(once:3000) // 4385号警告信息仅报告一次
  #pragma warning(error:2000) // 把164号警告信息作为一个错误。
#pragma pack(n)
  改变C编译器的字节对齐方式。一般用于结构体
  内存对齐,程序员可以通过预编译命令#pragma pack(n),n=1,2,4,8,16来改变这一系数,其中的n就是你要指定的"对齐系数"。

  #pragma  pack 1           作用:调整结构体的边界对齐,让其以一个字节对齐;<使结构体按1字节方式对齐>,取消各个结构成员之间的填充字节
  #pragma  pack ()
  
  例如:
  1 #pragma pack(1)
  2
  3 struct sample
  4 {
  5 char a;
  6 double b;
  7 };
  8
  9 #pragma pack()
  注:若不用#pragma pack(1)#pragma pack()括起来,则sample按编译器默认方式对齐(成员中size最大的那个)。即按8字节(double)对齐,则sizeof(sample)==16.成员char a占了8个字节(其中7个是空字节);若用#pragma pack(1),则sample1字节方式对齐sizeof(sample)==9.(无空字节),比较节省空间啦,有些场合还可使结构体更易于控制。

#pragma comment
  将一个注释记录放入一个对象文件或可执行文件中。注释类型:compiler、exestr、lib、linker。
  经常用到的是#pragma comment(lib,"*.lib")这类的。#pragma comment(lib,"Ws2_32.lib")表示链接Ws2_32.lib这个库。 和在工程设置里写上链入Ws2_32.lib的效果一样,不过用这种方法,别人在使用你的代码的时候就不用再设置工程settings了

10. 结构体 vs 类

参考:结构体_百度百科
结构体(struct):数据集合。类与结构体本质是类型,而不是数据,所以不存在于内存中,不能被直接操作,只有被实例化时,才会变得可操作。
结构体的定义如下所示,struct为结构体关键字,tag为结构体的标志,member-list为结构体成员列表,其必须列出其所有成员;variable-list为此结构体声明的变量。
struct tag {
   member-list
} variable-list ;
在一般情况下,tag、member-list、variable-list这3部分至少要出现2个。
在C语言中,结构体不能包含函数。C++的结构体可以包含函数,这样,C++的结构体也具有类的功能,与class不同的是,结构体包含的函数默认为public,而不是private。
…………
类与结构体在C++中有三点区别。
(1)class中默认的成员访问权限是private的,而struct中则是public的。
(2)从class继承默认是private继承,而从struct继承默认是public继承。
(3)C++的结构体声明不必有struct关键字,而C语言的结构体声明必须带有关键字(使用typedef别名定义除外)。
…………

定义一个类
class类名
{
public:
  公有成员
private:
  私有成员
protected:
  保护成员
};
public/private/protected成员,彼此之间都可以访问。private表示私有,私有的意思就是除了class自己之外(不考虑友元的话),任何人都不可以直接使用,私有财产神圣不可侵犯嘛,即便是子女,朋友,都不可以使用。
成员函数可以在类内实现,也可以在类外实现。内部实现的成员函数被默认为加上了inline;外部实现的成员函数必须加上域操作符,即"类名::成员函数"。
构造函数和析构函数不能被显式地调用,只能由编译器自动调用。
有的种类只是一种抽象概念,现实中并没有实际存在的对应物。比如:假设所有的动物都会叫,我们可以定义一个类——"动物",定义类中的一个成员函数——"叫",我们知道猫的叫声,也知道狗的叫声,然而"动物"是如何"叫"的?——我们根本无法实现它。所以,我们引入了抽象类的概念,抽象类是无法被实例化的,无法声明抽象类的对象
C++中包含纯虚函数的类是抽象类;
纯虚函数的作用:在基类中不能对虚函数给出有意义的实现(让类先具有一个操作名称,而没有操作内容),而把它声明为纯虚函数,它的实现留给该基类的派生类去做。
纯虚函数:virtual ReturnType Function()= 0;
多态性
:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。在运行时,可以通过指向基类的指针,来调用实现派生类中的方法。在C++中,可以用虚函数,抽象类实现多态。
在C++中使用:冒号表示继承,如class A:public B;表示派生类A从基类B继承而来;
  class A:public B //基类以公有方式被继承;
  class A:private B //基类以私有方式被继承;
  class A:protected B //基类以受保护方式被继承;
如果没有访问控制符则默认为私有private继承。
不管基类以何种方式被继承,基类的私有成员,仍然保有其私有性,被派生的子类不能访问基类的私有成员
C++类(Class)总结
C++中的基类与派生类

C++中的继承(1) 三种继承方式

11. try…catch

参考:c++ try catch 问题
VC++:catch(…)方法能够捕获几乎所 有类型的异常对象。
try
{
  受保护语句;
  if(出现某种错误) throw 异常;
  //执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容
}
catch(…) //catch( int& value ) //catch( double value[] )
{
  异常处理语句;
  //除非try里面执行代码发生了异常,否则这里的代码不会执行
}
程序执行的流程有两种:
(1)没有异常:try→受保护语句→其他语句。
(2)有异常:try→受保护语句→throw异常→catch→异常处理语句。

12. 常见系统异常

参考:C++中常见的异常
C++中的异常处理
MFC异常与C++标准异常
异常处理(exception handling):内存分配不足、数组下标越界、运算溢出或除数为零、打开文件失败
bad_alloc:请求分配内存失败, operator new 或者 operator new []
bad_exception:函数异常,通常是函数运行错误,抛出的异常
bad_typeid:类型异常,通常用typeid操作符,作用于一个NULL指针时,而该指针是带有虚函数的类,这时抛出bad_typeid异常
bad_cast:转换异常,使用dynamic_cast转换引用失败的时候
C++头文件:
<exception>:定义了最通用的异常类,仅报告异常发生,不提供额外信息。
<stdexcept>:定义了标准异常类
  exception    最常见的问题
  runtime_error    运行时错误:仅在运行时才能检测到的问题
  range_error    运行时错误:生成的结果超出了有意义的值域范围
  overflow_error    运行时错误:计算上溢
  underflow_error    运行时错误:计算下溢
  logic_error    逻辑错误:可在运行前检测到的问题
  domain_error    逻辑错误:域错误
  invalid_argument    逻辑错误:无效参数
  length_error    逻辑错误:试图生成一个超出该类型最大长度的对象
  out_of_range    逻辑错误:使用一个超出有效范围的值
<new>:定义了bad_alloc异常类型
<type_info>:定义了bad_cast异常类型

13. IDE(集成开发环境,Integrated Development Environment )

是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。

14. MFC (微软基础类库,Microsoft Foundation Classes)

参考:MFC_百度百科
是微软公司提供的一个类库(class libraries),以C++类的形式封装了Windows API,并且包含一个应用程序框架,以减少应用程序开发人员的工作量。其中包含大量Windows句柄封装类和很多Windows的内建控件和组件的封装类。

描述

Windows句柄

windows句柄

MFC Object

窗口

HWND

hwnd

CWnd and CWnd-derived classes

设备上下文

HDC

hdc

CDC and CDC-derived classes

菜单

HMENU

hmenu

CMenu

HPEN

hpen

CGdiObject类,CPen和CPen-derived classes

刷子

HBRUSH

hbrush

CGdiObject类,CBrush和CBrush-derived classes

字体

HFONT

hfont

CGdiObject类,CFont和CFont-derived classes

位图

HBITMAP

hbitmap

CGdiObject类,CBitmap和CBitmap-derived classes

调色板

HPALETTE

hpalette

CGdiObject类,CPalette和CPalette-derived classes

区域

HRGN

hrgn

CGdiObject类,CRgn和CRgn-derived classes

图像列表

HimageLIST

himagelist

CimageList和CimageList-derived classes

套接字

SOCKET

socket

CSocket,CAsynSocket及其派生类

OBJECT分以下几类:
Windows对象,
设备上下文对象,
GDI对象(bitmap,brush,font,palette,pen,rgn),
菜单,
图像列表,
网络套接字接口。

15. CTime(时间) CTimeSpan(时间间隔)

MFC:CTime类和CTimeSpan类

16. static_cast

参考:static_cast, dynamic_cast, reinterpret_cast, const_cast区别比较
标准C++中有四个类型转换符(显式//强转之前加这4个类型转换符):
  static_cast <new_type> (expression) 静态转换
  dynamic_cast <new_type> (expression) 动态转换
  reinterpret_cast <new_type> (expression) 重解释转换
  const_cast <new_type> (expression)常量向非常量转换
上行转换(up-casting):(父类 *)子类
下行转换(down-casting):(子类 *)父类 不安全
C风格转换是"万能的转换",但需要程序员把握转换的安全性,编译器无能为力;
static_cast最接近于C风格转换,但在无关类指针转换时,编译器会报错,提升了安全性;
dynamic_cast要求转换类型必须是指针或引用,且在下行转换时要求基类是多态的(基类中包含至少一个虚函数),如果发现下行转换不安全,dynamic_cast返回一个null指针,dynamic_cast总是认为void*之间的转换是安全的;
reinterpret_cast可以对无关类指针进行转换,甚至可以直接将整型值转成指针,这种转换是底层的,有较强的平台依赖性,可移植性差;
const_cast可以将常量转成非常量,但不会破坏原常量的const属性,只是返回一个去掉const的变量。

17. TRACE

参考:TRACE、TRACE0、TRACE1、TRACE2、TRACE3
TRACE()的用法总结
在debug下,可利用TRACE,打印出某些变量,方便调试,类似printf。
比如:
MFC:TRACE( "The value of x is %d ", x );
C++写法:
  #include <atltrace.h>
  #define TRACE ATLTRACE
  TRACE(L"%s/t%d/n", string, number);

18. UpdateData(true);

参考:UpdateData(TRUE)和UpdateData(FALSE)的区别

VC++/MFC
UpdateData(true); //用于将屏幕上控件中的数据交换到变量中,前提:为控件关联上变量才行。
UpdateData(false); //用于将数据在屏幕中对应控件中显示出来,前提:为控件关联上变量才行。

19. _T

_T("")是一个宏,定义于tchar.h下。作用是让你的程序支持Unicode编码。
不常用,可参考:绕死你不偿命的UNICODE、_UNICODE、__TEXT、__T、_T、_TEXT、TEXT宏

20. 字符集ASCII与UNICODE

参考:所谓编码--泛谈ASCII、Unicode、UTF8、UTF16、UCS-2等编码格式
字符编码笔记:ASCII,Unicode 和 UTF-8
ANSI单字节;
UNICODE双字节;将世界上所有的符号都纳入其中,每一个符号都给予一个独一无二的编码;覆盖全面,但会造成存储浪费。由此产生UTF(UnicodeTransformationFormat的缩写,意为Unicode转换格式)。UTF-8是一种变长的编码方式;是Unicode 的实现方式之一。

21. #error

C/C++语言的预处理命令之一,当预处理器预处理到#error命令时将停止编译并输出用户自定义的错误消息。
#error 用户自定义的错误消息。
---eg:
#ifndef __cplusplus
#error 亲,您当前使用的不是C++编译器噢!
#endif
#include <stdio.h>
int main()
{
  printf("Hello,World!");
  return 0;
}

#pragma  pack 1           作用:调整结构体的边界对齐,让其以一个字节对齐;<使结构体按1字节方式对齐>,取消各个结构成员之间的填充字节
#pragma  pack
()

例如:
1 #pragma pack(1)
2
3 struct sample
4 {
5 char a;
6 double b;
7 };
8
9 #pragma pack()
注:若不用#pragma pack(1)#pragma pack()括起来,则sample按编译器默认方式对齐(成员中size最大的那个)。即按8字节(double)对齐,则sizeof(sample)==16.成员char a占了8个字节(其中7个是空字节);若用#pragma pack(1),则sample1字节方式对齐sizeof(sample)==9.(无空字节),比较节省空间啦,有些场合还可使结构体更易于控制。

原文地址:https://www.cnblogs.com/yeyeye123/p/10807945.html