马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 moc 于 2018-10-29 18:57 编辑
1、预编译
#pragma once 和 #ifndef... #define... #endif
示例:
//方式一
#pragmaonce
... ... // 声明、定义语句
//方式二
#ifndef __SOMEFILE_H__
#define __SOMEFILE_H__
... ... // 声明、定义语句
#endif
#ifndef的方式受C/C++语言标准支持。它不仅可以保证同一个文件不会被包含多次,也能保证内容完全相同的两个文件(或者代码片段)不会被不小心同时包含。
当然,缺点就是如果不同头文件中的宏名不小心“撞车”,可能就会导致你看到头文件明明存在,但编译器却硬说找不到声明的状况——这种情况有时非常让人郁闷。
#pragma once 一般由编译器提供保证:同一个文件不会被包含多次。注意这里所说的“同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。带来的好处是,你不必再费劲想个宏名了,当然也就不会出现宏名碰撞引发的奇怪问题。对应的缺点就是如果某个头文件有多份拷贝,本方法不能保证他们不被重复包含。当然,相比宏名碰撞引发的“找不到声明”的问题,重复包含更容易被发现并修正。
方式二由语言支持所以移植性好,方式一可以避免名字冲突。
2、运算符~和!
!:称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。例:!(A && B)。
~:一元运算符,按位取反,具有"翻转"位效果,即0变成1,1变成0。
注意:即使bool flage = 1; 在flage = ~flage后,flage认为1。因为bool在内存中也占一个字节。
3、宏
1、宏定义
1. 简单的宏定义:
#define <宏名> <字符串>
例: #define PI 3.1415926
2 .带参数的宏定义
#define <宏名> (<参数表>) <宏体>
例: #define A(x) x
一个标识符被宏定义后,该标识符便是一个宏名。这时,在程序中出现的是宏名,在该程序被编译前,先将宏名用被定义的字符串替换,这称为宏替换,替换后才进行编译,宏替换是简单的替换。
2、宏替换发生的时机
为了能够真正理解#define的作用,让我们来了解一下对C语言源程序的处理过程。当我们在一个集成的开发环境中将编写好的源程序进行编译时,实际经过了预处理、编译、汇编和连接几个过程。其中预处理器产生编译器的输入,它实现以下的功能:
(1)文件包含
可以把源程序中的#include 扩展为文件正文,即把包含的.h文件找到并展开到#include 所在处。
(2)条件编译
预处理器根据#if和#ifdef等编译命令及其后的条件,将源程序中的某部分包含进来或排除在外,通常把排除在外的语句转换成空行。
(3)宏展开
预处理器将源程序文件中出现的对宏的引用展开成相应的宏定义,即本文所说的#define的功能,由预处理器来完成。
经过预处理器处理的源程序与之前的源程序有所有不同,在这个阶段所进行的工作只是纯粹的替换与展开,没有任何计算功能,所以在学习#define命令时只要能真正理解这一点,这样才不会对此命令引起误解并误用。
3、常见问题
例1:#define N 2+2
void main()
{
int a=N*N;
printf(“%d”,a);
}
宏展开是在预处理阶段完成的,这个阶段把替换文本只是看作一个字符串,并不会有任何的计算发生,在展开时是在宏N出现的地方 只是简单地使用串2+2来代替N,并不会增添任何的符号,所以对该程序展开后的结果是a=2+2*2+2,计算后=8,这就是宏替换的实质。
例2:#define area(x) x*x
void main()
{
int y = area(2+2);
printf(“%d”,y);
}
在这个程序里,2+2即为area宏中的参数,应该由它来替换宏定义中的x,即替换成2+2*2+2=8了。#define area(x) ((x)*(x)),不要觉得这没必要,没有它,是不行的。要想能够真正使用好宏定义,那么在读别人的程序时,一定要记住先将程序中对宏的使用全部替换成它所代表的字符串,不要自作主张地添加任何其他符号,完全展开后再进行相应的计算,就不会写错运行结果。 如果是自己编程使用宏替换,则在使用简单宏定义时,当字符串中不只一个符号时,加上括号表现出优先级,如果是带参数的宏定义,则要给宏体中的每个参数加上括号,并在整个宏体上再加一个括号。
4、宏中的特殊符号 #、##、 #@
1. #define Conn(x,y) x##y
x##y表示x连接y,举例说:
int n = Conn(123,456); // 结果就是n=123456;
char* str = Conn("abc", "123"); // 结果就是 str = "abc123";
2. #define ToChar(x) #@x
#@x,就是给x加上单引号,结果返回是一个const char。
char a = ToChar(1); //结果就是a='1';
3. #define ToString(x) #x
#x是给x加双引号
char* str = ToString(123132); //str="123132";
4、全局变量的管理 与 extern
项目中不同cpp文件中的全局变量,都是在全局作用于下,如果相互include,会产生重定义。
可以将全局变量统一管理,项目中用extern.cpp与extern.h统一管理。
extern.cpp// 把所有全局变量的定义都放在该文件夹下。统一管理
int g_a = 10;
int g_b = 20;
int g_c = 30;
extern.h// 把所有全局变量用extern扩展,然后在要用到这些变量的文件中只需#include “extern.h” 即可。
#pragma once
extern int g_a;
extern int g_b;
extern int g_c;
5、Visual studio 自定义补全代码
1.找到补全代码的文件所在的位置 工具--> 代码片段管理器 --> 语言(visual C++)
2.定位到该路径,复制一份if.snippet文件到桌面,双击打开(打开后拖动文件到所打开的窗口)
3.在xml格式的文件中修改Title,Shortcut,CDATA标签中的内容,修改后保存,并重命名文件,如#1.snippet.
4.把修改名称的文件放入1中找到的位置。这样在Visual studio编译器中就可以使用#1来补全代码。
6、C++11新特性 auto遍历
像python中的for ... in ...,遍历容器一样,C++11的新特性中也有了该功能:for (auto each : 容器) {...;}
1. 遍历字符串:std::string str = “hello, world”;
for(auto ch : str) {
std::cout << ch << std::endl;
}
2. 遍历数组int arr[] = {1, 2, 3, 4};
for(auto i : arr) {
std::cout<< i << std::endl;
}
3.遍历stl 容器std::vector<std::string> str_vec = {“i”, “like”, "google”};
for(auto& it : str_vec) {
it = “c++”;
}
4. 遍历stl map (遍历map返回的是pair变量,不是迭代器。)std::map<int, std::string> hash_map = {{1, “c++”}, {2, “java”}, {3, “python”}};
for(auto it : hash_map) {
std::cout << it.first << “\t” << it.second << std::endl;
}
|