命名空间
主要是为了解决名称冲突的问题
引用
变量的别名
引用和指针的区别:
1.指针是个变量,可以把它再赋值指向别的地址
2.建立引用的时候必须初始化,并且绝不会再关联其他不同的变量。而指针可以一开始就赋值空,之后可以再改变指向其他地方。
由于指针也是变量,所以,可以有指针变量的引用,比如下面的看着有点诡异的代码
int* a = NULL; int*& ra = a;//表示int*的引用ra初始化为a int b = 9; ra = &b;//OK,ra是a的一个别名,是一个指针
- void引用是不合法的
void& a = 3;void只是在语法上相当于一个类型,本质上不是类型,没有任何一个变量或者引用的类型的是void
- 不能建立引用的数组
int a[10] ={0}; int& ra[10] = a;//error
- 没有引用的指针和引用的引用
int a ; int& ra = a; int& *p = &ra;//error启动定义一个引用的指针
- 有空指针,但没有空引用哇
无符号数Unsigned
这个操蛋的地方在于不能出现一个负值,如果一个int 和一个 unsigned出现在一个表达式里面的话会出现意想不到的效果,比如这个int是一个负值的时候,就会自动转换为unsigned,也就是通常会变成一个很大的整数;在比如一个负的unsigned和一个正的int进行比较的话,unsigned也会自动转换成一个合法的unsigned。
#include <iostream> using namespace std; int main(){ int i = 2; unsigned j = -1; if (i > j) cout << "is true" << endl; else cout << "is false" << endl; cout << "j = " << j << endl; return 0; }
is false
j = 4294967295
所以一定切记,不要让unsigned成为一个负数
引用的参数传递
- 传递引用给函数与传递指针的效果是一样的。都能改变变量的值
- 用引用作为参数比使用指针有更清晰的语法
- 使用引用作为参数返回值,给函数带来的意义
1.函数只能返回一个值,如果要返回两个值怎么办?可以用引用给函数传递两个参数,然后由函数往目标中填入正确的值
2.函数返回值时要返回一个值的副本,而用引用返回时不需要生成副本,所以提高了效率
注意:如果返回不在作用域范围内的变量或者引用那就有问题了。这与返回一个局部作用域指针的性质一样严重,比如
指针
关于常量指针
int a = 1 , b = 2;
const int *p = &a; //p是表示指向一个const int数,不过指向一个非const的int也是可以的
p = &b; //是可以指向别的对象的,这里的const是底层const
*p = 5;// error !!
其实可以这样理解,const int *p 就表示它要指向一个const int 而这个数是不能被改变的,所以理所应当,*p是不能变的,它不需要去关心那个数到底是不是const,虽然前面也说,p是可以指向非const的
而如果换成
int *const p = &a; //这里的p就是代表一个指向int的常量指针,这里的const称作顶层const,修饰的是p
p = &b; // error , p是const不能改变,所以p不能再指向其他地方了
*p = 434; // 可以,int是可以改变的
const int *const p = &a ; //当然这样就 *p 和p都不能改变了
constexpr
常量表达式。如果修饰的是指针的话,那么只跟指针有关,跟指针所指的对象无关,比如
int i = 3 , j = 22;
constexpr int *p = &i; //这里可以指向const 也可以指向非const
p = &j; // error
*p = 4342; //可以
auto
可以让编译器替我们分析表达式所属的类型。
比如:
auto a = 4 + 2; auto da = 4.3 + 3.3; cout << "a = " << a << endl; //6 cout << "da = " << da << endl;//7.6
这个东西注意一个地方,auto能在一条语句中声明多个变量,但是这一条语句中的所有变量都必须同一个类型。
你不能这样搞:auto s = 0 , f = 3.3; // ERROR
string
1.string的size函数返回的不是一个int!!!!返回的是一个string::size_type类型的值。尽管不太清楚这个类型的细节,但是可以肯定:它是一个unsigned类型的值。所以通常用auto或者decltype来推断。比如统计一个string里面的标点符号
#include <iostream> using namespace std; int main(){ string str = "Hello World!!!!"; decltype(str.size()) punct_cnt = 0; for (auto c : str) if (ispunct(c)) punct_cnt++; cout << punct_cnt << " punct in str" << endl; return 0; }
4 punct in str
一个问题
#include <iostream> #include <vector> using namespace std; int main(){ unsigned scores[11]; unsigned grade; while (cin >> grade) if (grade <= 100) ++scores[grade/10]; for (auto i : scores) cout << i << endl; return 0; }
为什么会有问题呢??? 为什么scores一定要初始化呢?scroes[11] = {};
强制转换
形式如下
cast-name<type>(expression); type是要转换的目标类型,expression是待转换的值。cast-name是static_cast、dynamic_cast、const_cast、reinterpret_cast中一中
const_cast
改变运算对象的底层const,将常量对象转换成非常量对象的行为。举个例子
#include <iostream> using namespace std; int main(){ int i = 3; const int *ip = &i; cout << *ip << endl; //*ip = 423; //cout << *ip << endl; //错误 *ip不能变 int *p = const_cast<int*>(ip); *p = 23; cout << *p << endl; //23 cout << "i = " << i << endl; // i = 23 return 0; }
上面本来是不能通过*p来改变对象的值的,但是const_cast等于是将const去掉了获取了写权限
要注意的是 i 本来就是可以改变的 ,如果把 i 改成const ,如:const int i = 3; 其他都不变,那么程序编译运行是没有问题的, *p也还是23 ,但是 i 不会变,还是3。
分离式编译
即允许我们把程序分割到几个文件中去,每个文件单独编译。程序复杂的时候,会希望把程序各个部分分别存储到不同的文件中。比如:
在同一个目录下,放了自己写的头文件 test.h ,还有 test.h 中声明的函数定义文件 fun.cpp , 以及放了main函数的 t.cpp
#include <iostream> #include "test.h" using namespace std; int main(){ cout << fun(4) << endl; return 0; }
#include "test.h" int fun(int i){ return 2*i; }
int fun(int i);
分离式编译的步骤如下:
g++ -c t.cpp fun.cpp
这里的-c参数是 Compile and assemble, but do not link(编译并汇编,但不链接),编译成功后会输出2个.o的文件,一个是t.o,一个是fun.o
然后再链接成一个可执行文件
g++ t.o fun.o -o t
后面这个 t 是代表要生成的可执行文件的名字,这个参数是必须的,不能省,完成后会生成一个名为t的可执行文件