关于VS2003IDE的使用技巧大全 之(二)

来源:互联网 发布:spark金融数据分析 编辑:程序博客网 时间:2024/05/17 05:05

//////////////////////////////////////////////////////////////////////////
VS2003 实用快捷键.这些快捷键都是老子经常用到的...呵呵.
shift+F11:debug时跳出当前函数
ctrl+alt+Q:在debug单步执行时期,查看光标所在位置的变量的内部值!!!此快捷键相当重要.
Ctrl+J:List members,当正在写代码时,写到一个类的内部成员时,而此时,编译器又没有提示时,用定位错误:F4
CTRL+I:实时搜索.直接输入函数名进行搜索
CTRL+J,调出提示窗口,Eg:pWnd->(这里用CTRL+J)
Comment Selection/Comment a line: Ctrl+K+C
uncomment Selection:Ctrl+K+U
Bookmark:ctrl+k+k;
previous bookmark crl+k+p
next bookmark ctrl+k+n
Clear all bookmarks Ctrl+K,L
watch param info:ctrl+shift+space
quick info:ctrl+k+i
Stop dubugging F7
alt+right arrow 完成全字的拼写.
Ctrl+F11:切换到所汇编,与源文件。
F7,编译/停止调试。->F5不编译直接运行
 
编辑.查找 CTRL + F 显示“查找”对话框。   
编辑.在文件中查找 CTRL + SHIFT + F 显示“在文件中查找”对话框。   
编辑.查找下一个 F3 
   
定位到查找输入框 Ctrl+d  

navigate backward:CTRL+-
navigate forward:ctrl+shift+-
快速光标左右移:CTRL+leftArrow,CTRL+rightArrow
光标不动,屏幕滚动CTRL+UPArrow,DownArrow.
Ctrl+R+R:将当前屏内所有太长一行代码分为两行.

让那些查找窗口,output窗口等,在需要的时候自己出来,,不需要的时候隐藏。(通过窗口左上角有个定位的tag,单击一下即可,就像,VC的属性页的位置可定可不定一样。)
所有内容以outline显示:toggle all outline.一块outline:ctrl+M.

所谓的TEXT文件与 binary文件的不同是:如果输入/n,text文件会将其视为一个换行,而,binary文件只会将其视为一个二进制数。!
/////////////////////////////////////////////////////////////////////////////////////////
为什么说  对于错误检测,使用 if()判断,并做出错处理,要比简单的assert要好.
因为,assert()仅在debug版下有效,在release 下,该语句会出错.
如果,我们的程序,开发总是在debug下进行.只是在最后发布的时候,release.那么,使用assert是没有错的.
但是,亲爱的程序员,请想一下这个问题.假定你一个函数要取得一个文件.assert(文件名不空.).
当自己开发测试时,如果文件名为空.你会及时的发现这个错误.
但是当你发布release版本后.客户不小心,把那个文件名字给删了.这时程序就运行不正常的..至于在哪出的错,你会找不到的...所以,不如,把做成if(false) return false. 外面做个Messagebox("")然后 exit()或者,写个出错程序,让程序更健壮!!!.
/////////////////////////////////////////////////////////////////////////////////////////"
/////////////////////////////////////////////////////////////////////////////////////////
对于搜索算法的认识:
我在迷宫中所用的寻路算法,是,盲目的,毫无策略,毫无启发式的,寻最短路径算法.
当要求出最短路径时,设迷宫为15*15的方格,要...N年才能计算得出结果.!!!因为,基本上要4^15次方次计算才能求出结果,这是一个天文数字!!!虽然,总是递归深度不很深.(测试时大概是40深)

蚁群算法,是同时进行多个探索进程.每个进程都不用回朔!(这是关键,如果回朔的话,,所用的时间就是几何级增长了.)
自己按照自己的算法不断的向前找..当找到目标时.该进程也同时记录了自己找到目标所经过的路径.在该路径上洒下信息素.
下次出发找时.就受到这些信息素的指引.但不全按照信息素的方向行进.(不然的话,是找不到最短路径的)
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
应用程序正常初始化失败 
多是访问 保护内存空间,
检查程序中的 指针(重点) 等!!

可能是 LoadLibrary 时候,
是否正确的导入了库?
GetProcessAdress 是否正常 ?  断点查看一下是否为 NULL,
如果为 NULL , 还继续运行, 那么自然就是这么个错误 !

这种错误的话, 确认一下导入时候的函数名是否正确 ~~ 
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
// :这样子重载 == 算符有个缺陷!!!,当左边是个DLPoint常量时,不能进行比较运算!!!
 bool operator == (const DLPoint& rhs) 
 {
  return ( (x == rhs.x) && (y == rhs.y) );
 }
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
关于stl:
1:
reverse():保留一个存储区域.常用来重新申请内存..
capacity():查询当前保留的区域.不常用
size():查询当前容器中已有元素个数.
2:当使用copy算法 作用于vector上时..必须,保证,目标的vector中size() 已有的元素不少于源 vector元素,
  否则,copy 不进去!!!.妈的!
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
对编写递归函数的几点经验:
1:对递归函数的参数,慎用 引用 传递,如果是引用,本次递归的某个中间变量,会影响到下次递归的变量.!!!
  曾经这么一个小错误让我很痛苦!
2:写一个递归函数时,把该函数的任务明确的定义出来.!!!
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
对const的几个简单却最易犯错的认识!
1:如果传进来一个const引用参数,
那么,只能使用用这个参数的常方法!!!,否则报错!
eg:
bold ValidPos(const DLPoint& pos)
{
 ...
 pos.GetX(); //注!!,这里GetX必须是DLPoint的常方法.
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
对 boost 库的更深认识
smart_prt 是个模板, copy过来就可直接用.
别去研究boost代码,把 STL 研究完了再说吧.
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
关于关的隐式自动类型转换:
即 operator **** (){}****即返回值,即想要转换为的对象.
eg operator char* (){}转换为char*指针.
eg operator Teacher (){} 转换为Teacher 对象
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
什么是函数对象?
即工作起来像函数一样的长像,当然,长得也跟函数一模一样.
比如,你有一个函数 char* Fun(void) 无参,且返回 char*的函数
像这样
char *Fun()
{
 return p=new char[10];
}
如果写成一个函数对象,就成了这样子.
class Fun
{
public:
  char* operator() (void) // 第一个()是()算符.第二个()其中要包含其接收的参数.
  {
 return p=new char[10];

  }
}
这样,这个 对象使用跟上边的那个函数一模一样...
在STL中,通用算法,本质上接收的是一个函数.而你呢.使用函数可以,使用函数对象也可以.
如果STL中,通用算法本质上接收是一个函数对象,那么,你用函数作为参数就不对了,不是吗?
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
对dll的深入认识.
1:dll可用来进程间通信
默认的情况下,这个应用程序只是共享dll中代码,没有共享其中数据.
可以在 dll中设定全局变量,并使用share宏,使多个进程共享 dll中数据.
2:dll本质原理.
在创建Windows程序时,链接过程并不把DLLs文件链接到程序上。直到程
序运行并调用一个DLLs中的函数时,该程序才要求这个函数的地址。此时Windows才在DLLs中寻找被调用函数,并把它的地址传送给调用程序。采用这种方法,DLLs达到了复用代码的极限。

/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
关于字符串的保存方式(指针(即不需要储存位置),char[],string?),传递方式("常量字符号"?全局静态字符串?宏(常量的变种)?来自一个批针),
怎么用(长期用?作为中转传给别的函数?)

// :
示例:
1:win32工程.
程序标题的来源(第一次存储)  : 一个宏.
标题字符串二次的存储: 一个全局字符数据 char[MAX_LEN_APPNAME]

strlen():取得字符串长度,不计最后一个/0字符
strcpy(),第一个字符缓冲区,必须是(后一个字符长度+1)!!!,不然,然破坏strcpy调用堆栈,函数无法正常返回!!!缓冲区溢出!
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
永远不要在头文件中定义变量,哪怕是static变量.
当你在头文件中定义一个全局变量.
当这个头文件被任意两个CPP文件包含时,会出现变量重定义的错误!
当你在头文件中定义一个静态全局变量.
当这个头文件被任意两个CPP文件包含时,虽不会出现变量重定义的错误,但这个static在两个CPP中存在了两份拷贝!!浪费内存.(实事上,静态全局变量是经过了名字改编而已.在哪道听途说的忘了,呵呵)

另外注意1:!!!.h文件是不会参与编译的,,所以,就算你的.h文件里语法错误连篇,只要该.h文件没有被某个cpp文件包含.就不会报错,哈哈...这就是为何,有时候,一直编译没错,包含进入一cpp时,,,天啊,,,崩溃了,,,100个错误.哈哈.
所以,两个不同的.h文件,都定义了一个同名的全局变量,,只要这两个文件没有被别的 cpp分别包含,是不会报错的,哈哈.

别外注意2:!!!当你想有一组信息,要多个cpp文件共享时,怎么办?
1:使用const.EG:const int LEN = 20;
    const 是一种特殊的变量,其默认情况下是static,即静态的(不信??,不信就去定义一个看看,在VS2003类视图里,把鼠标放在const变量上面,会出现提示: static const int LEN.,呵呵)
    虽然这样子会出错浪费,但是如果没有字符串出现的话,这样的良费还是可以接受的,呵呵
如果,你觉得浪费空间,那么就用宏吧.效果一样的,,,除了debug期间会不点不爽之外,呵呵.

2:使用枚举类型的声明.EG enum{LEN = 20}; 重点推存!!!.可惜,只能定义整型
注意,一个变量被多次声明是没有问题的,呵呵,一个小技巧.注意,千万别写成 enum{LEN = 20}d;了,这样,d就是定义一个变量了.
你想一下,当你多次声明一个函数,一个类时,会不会报错,更何况有一种专门的技巧叫做前置声明呢(用来减少文件交叉引用)

3:现在,不得不放出我的杀手jian了.最后一个技巧,也是高手们最常用的技巧.!!!
如果你想定义一系列字符串,显然,用const,enum是不行的,用#define?喔,是的,可以,但是我不喜欢.
我们的技巧就是,定义一个.h,一个.cpp,把这些字符串定义都放在 cpp里面.
在.h里面.对这些字符串做extern声明.然后,想引用这此字符串的cpp文件,只需包含该.h文件即可.既不浪费内存,又方便.
事实上,这个技巧,可用在很多方面.比如一个全局的类...等.用是全局日志输出文件上特别好.嘿嘿.
/////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
source insight
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
请对下面程序进行改进
int test(int a)
{
int b;
b=2*a;//可能造成数据溢出可将int b改为long b,返回类型改为long
return b;
}
>>>>:
int test(int a)
{
    return a<<1;// >>1:乘2. 另外,还可能溢出.
}
////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////
关注:
一个好的程序员不应该把所有的判断交给编译器和调试器,应该在程序中自己加以程序保护和错误定位,具体措施包括:

对于所有有返回值的函数,都应该检查返回值,除非你确信这个函数调用绝对不会出错,或者不关心它是否出错。
一些函数返回错误,需要用其他函数获得错误的具体信息。例如accept返回INVALID_SOCKET表示accept失败,为了查明 具体的失败原因,应该立刻用WSAGetLastError获得错误码,并针对性的解决问题。
有些函数通过异常机制抛出错误,应该用TRY-CATCH语句来检查错误
程序员对于能处理的错误,应该自己在底层处理,对于不能处理的,应该报告给用户让他们决定怎么处理。如果程序出了异常, 却不对返回值和其他机制返回的错误信息进行判断,只能是加大了找错误的难度。
/////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////
定位程序崩溃地址:
1:记下崩溃地址,这个地址是64bit位,后32位均为0,所以,只记低32位即可
EG:Address: 0x000000000040141a---->注:这个地址是产生异常的那行代码 在生成的代码文件(即exe)中的地址.而非运行时在内存时的地址,要是那样的话,这个地址就每次都不是一样了.
取后8位即:0x0040141a
减去基地址 0x00400000---->第个程序加载时,都有个基地址.(具体对不对,看核心编程)
再减去PE文件格式规定的那个地址 0x00001000
也就是说,一般来说,如果代码量不是很大的话,一般,崩溃地址的后3位即是 崩溃代码所在的行的在具体件中的地址.
2:生成map文件.
修改编译选项:Linker->debugger->3个关于map的选项设为yes,并填写生成map文件的文件名.
修改编译选项:Linker->general->Enable incremental linking->NO,禁止增量连接(这个选项与生成map line选项有冲突)
3:查看map文件.
1根据0040141a找到崩溃函数.eg:crash()
2从程序中找崩溃函数所在的文件.EG:testCrash.cpp
3查找文件和map line信息.并对应0x41a找到崩崩溃的代码所在行号!!

/////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////////////////////////////////////////////
现在在对预编译文件的使用有了更深刻的认识.
但凡放入预编译中的头文件,就相当于,本工程所有其他的.h文件中都包含了该文件.不用再包含.
但,所有Cpp文件中必须均要包含stdafx.h文件,及所用到的且stdafx.h文件中没有包含的头文件.SHG就是例证.

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
关于转型:
1:static_cast<>
 EG:flaot->int,
 EG:static_cast<RENDER_FUNC>(renderfunc) // renderfunc是一个int值
 EG:ms_Singleton = static_cast< T* >( this );// 这是为何????还能转型指针????
2:reinterpret_cast<>
 EG:A *pA = reinterpret_cast<A*>(pData);// pData是个字符数组.

/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
几个比较郁闷的问题:
定义类的时候,在类的最后一个}后要加分号.
声明函数的时候,尾部要加分号.

实现函数时,所有预编译指令时(eg include define),后面可均不要分号.加上也无碍.

当出现莫明其妙的问题的时候,99%的原因是少写了 分号;
1:可能是类定义少了分号2
2:可能是枚举变量少了分号
3:可以是上面包含的文件的类定义中少了分号.(但在头文件中并不报错,错误有传递性!!!)
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
m_dwAttribute |= GUIATT_ENABLE;  // :将GUIATE_ENABLE 属性并入 m_dwAttribute中
m_dwAttribute &= ~GUIATT_ENABLE; // :去除
if ( m_dwAttribute & GUIATT_ENABLE ) // :查询是否包含
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
关于出错处理:
告别学生时代的出错处理.
用一种规范化的,可维护的方式处理.

加外,什么时候一个函数应该返回bool,什么时候void
eg:当一个函数执行失败,会导致后续的工作无法进行下去时,,,用bool
eg: file.open()
    CreateFileFormFinename()

 大多数扫尾性质的函数,都应该返回void.
   Release()
   SetX,Set...
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
最经典的dubug版的程序崩溃.
()
在几乎找不到自己的程序有哪些做错的情况下,每次程序退出时,
程序都出错,说发现一个未处理的异常,删除一个指针时出错.
可是这个指针明明是个非空指针,为什么不能删除呢?

原因在于,这个指针是存在于 DLL 中的指针,删除指针的操作也是
在 DLL 中的代码中执行的.当然,你的DLL代码写得并没有错,那么错在哪了呢?

原来,在编写 DLL 时,用 #define NTM_API __declspec(dllexport),导出DLL
但我们在写应用程序时,就该用 #define NTM_API __declspec(dllimport),可是,由于 SHG,与 NTMGraphics
应用程序是在同一个解决方案里面,引用的都是同一个 shg.h文件.所以,只能 定义 NTM_API 为其上之一.
由于应用程序是 用的是 #define NTM_API __declspec(dllexport),所以,会出现,
1:Unhandled exception at 0x7c921230 in NTMGraphics_d.exe: User breakpoint.
1.2:在output窗口中显示:
 HEAP[NTMGraphics_d.exe]: Invalid Address specified to RtlValidateHeap( 003D0000, 003F4D38 )
2:定位到出错代码: STL::list链表容器
  ~list()
  { // destroy the object
  _Tidy();
  }
3:查看CallStack:
GUIManager::`vector deleting destructor':
4:再上一步:
template <class T>
void SafeDelete(T & tPtr)
{
 if(NULL != tPtr)
 {
  delete tPtr;
  tPtr = NULL;
 }
}     
/////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////
关于对内存分配机制的认识.
对于一个类,如果用new,malloc.等.则是利用了C++内置的内存分配模型机制.
如果针对该类的特点,比如说,该类是个链表,就可为该类专门写一个合适该类的内存分配模型,
底层的分配内存的机制,还可以是new,也可以是malloc,更可以是一种新的分配形式.但这里更关键的是内存的管理策略.对分配出来的内存如何管理,何时回收.通过自己管理内存,可以减少由于频繁分配内存带来的时间开销,又可防止太多的碎片的产生,减少空间的开销.
也就是说,同一个Allocator(),的不同的使用策略,可适应不同的需求,EG, array,list,map等.

-->更牛B的,你可以自定义一个New函数,完成内存分配,但关键是这个New有自己的内存管理机制.
这里面有一个new 的新语法.new (要进行填充数据的地址)(对前述地址的内存进行某些动作)

对new的重载,与 使用allocator 的区别.
现在看来,重载new.delete,是为了改变对已早请的内存的使用简略.对类的构造函数的调用,还是由new负责.
利用allocator机制.包括对内存的申请,对早请的内存的调用构造函数初始化,责任全在于allocator,
其中,想要实现对内存的数据填充,要用到 new 的新语法.new (要进行填充数据的地址)(对前述地址的内存进行某些动作)

/////////////////////////////////////////////////////////////////////////////////////////////

 

关于vsutio.使用大全
0:
对于加入到工程中的cpp文件,应该检查是否在第一行显式的包含stdafx.h头文件,这是Microsoft Visual Studio为了加快编译 速度而设置的预编译头文件。在这个#include "stdafx.h"行前面的所有代码将被忽略,所以其他头文件应该在这一行后面被包含。

关于预编译指令学习之二。
1 查看是否定义了预编译指令
if define(DEBUG)
 #pragma message("DEBUG have been defined.");
endif
1.5
想不用单步调试就能查看变量的值...
用OutputDebugString("");
OutputDebugString("该句在debug时输出,编译时不输出");// 想要该句运行要,要包含windows.h头文件.

2
#pragma ***(push)// 此时相当于把***相当的信息push进去.(即在栈中保存***一份)
...// 现在对***的信息进行修改
#pragma ***(pop)// 恢复***原来的信息.

#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等价于:
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
#pragma warning(once:4385) // 4385号警告信息仅报告一次
#pragma warning(error:164) // 把164号警告信息作为一个错误。


#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......这里是自己的代码.
#pragma warning( pop )
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。

 

#pragma pack和数据对齐问题
struct A{
char c;
int x;
};

void main()
{
    int len = sizeof(A);
    printf("sizeof(A)=%d/n", len);    // len = 8 ,默认是4字节对齐
}

#pragma pack(push)
#pragma pack(1) // 按1字节对齐
struct A{
char c;
int x;
};
#pragma pack(pop)

void main()
{
    int len = sizeof(A);
    printf("sizeof(A)=%d/n", len);    // len = 5