C++初阶------------------入门C++(二)
https://developer.aliyun.com/article/1499032
常性
当我们如果使用使用不同的引用类型去引用一些不同类型的变量
如
int i = 10; double j = i; const double& rj = i;
如果是使用 double&就会报错,为啥? 因为我们在使用不同类型进行接收的时候,i会产生一个临时变量,(类型转变才会产生临时变量)并且这个临时变量具有常性,需要用const的变量进行接收。
指针和引用的区别
引用:
- 语法上,没有开辟空间,在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
- 引用比指针使用起来相对更安全
- 没有NULL引用,但有NULL指针
- 有多级指针,但是没有多级引用
- . 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
#include<iostream> #include<time.h> #include<assert.h> using std::cout; using std::endl; using std::cin; int main() { int c = 20; const int& d = c; cout << sizeof(d); return 0; }
6. 指针语法上,开辟了空间,在底层实现上实际也是有空间的
7. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
两者共同点:
1.引用的底层是汇编实现的
引用表面好像是传值,其本质也是传地址,只是这个工作有编
译器来做
内联函数
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率。
#include<iostream> using std::cout; using std::endl; using std::cin; int Add(int a, int b) { return a + b; } int main() { Add(1, 2); return 0; }
这里是没有inline修饰,需要创建函数栈帧
使用inline修饰
#include<iostream> using std::cout; using std::endl; using std::cin; inline int Add(int a, int b) { int c = a + b; return c; } int main() { Add(1, 2); return 0; }
未显示展开
要想看到展开我们需要以下操作
内联函数 优点:
- 可以调试
- 效率高,会展开
- 不用创建栈帧,提高效率
缺点:
不适合于大型的函数,每次调用inline修饰过的函数,就会展开一次,如果函数有100行,调用10000次,合计就要运行100 * 10000行,没有inline修饰的函数,每调用一次,就会找到相同的函数栈帧进行调用,总次数就是 100(函数的行数) + 10000(反汇编的call),所以inline修饰大型函数就会影响可执行程序的大小
inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。(简单的理解,看编译器的心情来决定展不展开)
需要注意的是,如果在其他cpp文件使用inline修饰函数,再到头文件声明,在其他cpp文件使用这个函数就会报错,因为使用inline修饰的函数在链接时不会生成符号表。这是因为inline函数在编译时会被直接插入到调用它的地方,而不会产生独立的函数代码。
day1_1.cpp
#include"day1_1.h" int main() { fun(10); return 0; }
day1_2.cpp
#include<iostream> using std::cout; using std::endl; using std::cin; inline void fun(int a) { cout << a << endl; }
day_1.h
#include<iostream> using std::cout; using std::endl; using std::cin; inline void fun(int a);
所以我们使用inline修饰函数,在对当前cpp文件或者在头文件定义和声明就行了
auto
auto关键字可以用于自动推导变量的类型,让编译器根据变量的初始化表达式推导出其类型,从而简化代码书写
#include<iostream> using std::cout; using std::endl; using std::cin; int main() { int a = 10; int* b = NULL; auto c = a; auto& d = a; cout << typeid(a).name() << endl; cout << typeid(b).name() << endl; cout << typeid(c).name() << endl; cout << typeid(d).name() << endl; return 0; }
这里typeid(a).name()是返回a的类型
我们可以得出结论
- auto 必须初始化
- auto 不能当函数参数,返回值也不行
- auto不能声明数组
范围的for循环
与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。
前面我们学习过C语言的for循环语句,
int i = 0; for (i = 0; i < 100; i++) { printf("%d ", i); }
但是在c++中的for语句有点差别
#include<iostream> using std::cout; using std::endl; using std::cin; int main() { int a[] = { 1,2,3,4,5,6,7,8,9 }; for (auto e : a) { cout << e << " "; } return 0; }
意思就是遍历一遍a数组,每个元素都依次赋值给e,自动判断结束, 修改e不会修改里面的元素
如果我们要修改元素的值
#include<iostream> using std::cout; using std::endl; using std::cin; int main() { int a[] = { 1,2,3,4,5,6,7,8,9 }; for (auto& e : a) { e++; cout << e << " "; } return 0; }
我们可以使用引用来进行修改
指针空值nullptr
#include<iostream> using std::cout; using std::endl; using std::cin; void func(int) { cout << "f(int)" << endl; } void func(int*) { cout << "f(int*)" << endl; } int main() { func(0); func(NULL); return 0; }
这里的函数只写了类型,没有写变量,这个在c++是可以的,
看到结果的人可能会发现,为啥都打印了f(int),NULL不是指针类型吗,
其实不是,在c++
NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:
如果是c++就是把NULL定义成了一个宏,C语言就是一个指针类型
所以c++就为了弥补这个错误,就写出了一个nullptr来代表NULL空指针
注意:
- 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入
的。 - 在C++11中,sizeof(nullptr) 与 sizeof((void*)0)所占的字节数相同。
- 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr。