内联函数 inline function
- 使用inline修饰函数的声明或者实现,可以使其变成内联函数。
- 一般建议声明和实现都增加inline修饰,被inline修饰的函数
特点:
- 编译器会将函数调用直接展开为函数体代码,好处是可以减少函数调用的开销,但是会增大代码体积。
- 一般在频繁调用而且代码体积不大(不超过10行)的时候用。即使被inline修饰,但是函数体积过大,不会被编译器内联,比如递归函数
内联函数与宏
- 内联函数和宏,都可以减少函数调用的开销
- 对比宏,内联函数多了语法检测和函数特性
// 思考以下代码的区别
#define sum(x) (x + x)
inline int sum(int x) { return x + x; }
// 使用:
int a = 10; sum(a++);
#pragma once
- 我们经常使用#ifndef、#define、#endif来防止头文件的内容被重复包含
-
pragma once可以防止整个文件的内容被重复包含
- 区别:
-
ifndef、#define、#endif受C\C++标准的支持,不受编译器的任何限制
- 有些编译器不支持#pragma once(较老编译器不支持,如GCC 3.4版本之前),兼容性不够好
-
ifndef、#define、#endif可以针对一个文件中的部分代码,而#pragma once只能针对整个文件
-
引用(Reference)
- 在C语言中,使用指针(Pointer)可以间接获取、修改某个变量的值
- 在C++中,使用引用(Reference)可以起到跟指针类似的功能
- 引用存在的
价值之一
:比指针更安全
、函数返回值可以被赋值
int main() {
// 定义了一个引用,相当于是变量的别名
// rAge就是一个引用
int age = 2;
int &rAge = age;
int &rAge1 = rAge;
int &rAge2 = rAge1;
rAge = 11;
cout << age << endl;
rAge1 = 22;
cout << age << endl;
rAge2 = 33;
cout << age << endl;
return 0;
}
// 对别名的理解
int age = 10;
const int &rAge = age;
age = 30;
cout << "age is " << age << endl;
cout << "rAge is " << rAge << endl;
// log:
age is 30
rAge is 30
注意:引用的类型要跟引用对象一致,否则引用不会指向对象的地址,而是创建一个新的地址,如下:
int age = 10;
const long &rAge = age;
age = 30;
cout << "age is " << age << endl;
cout << "rAge is " << rAge << endl;
// log:
age is 30
rAge is 10
// 数组的引用
int array[] = { 10, 20, 30 };
int (&rArray)[3] = array;
int *a[4];
int (*b)[4];
// int (&rArray)[4] = array;会报错:
// Non-const lvalue reference to type 'int [4]' cannot bind to a value of unrelated type 'int [3]'
void swap(int a, int b) {
int temp = a;
a = b;
b = temp;
}
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int v1 = 10;
int v2 = 20;
swap(v1, v2);
cout << "v1 is " << v1 << endl;
cout << "v2 is " << v2 << endl;
}
// log:
v1 is 20
v2 is 10
注意点:
- 引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)
- 对引用做计算,就是对引用所指向的变量做计算
- 在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”
- 可以利用引用初始化另一个引用,相当于某个变量的多个别名
- 不存在【引用的引用、指向引用的指针、引用数组】
const
- const修饰的对变量,不可修改,永远是这个值
- 如果修饰的是类、结构体(的指针),其成员也不可以更改
// 以下5个指针的含义:
int age = 10;
const int * p0 = &age;
int const * p1 = &age;
int * const p2 = &age;
int const * const p3 = &age;
const int * const p4 = &age;
- 上面的指针问题可以用以下结论来解决:
- const修饰的是其右边的内容
- 因此:
- p0和p1是一样的效果,不可修改【value】,可以修改指向
- p3和p4是一个的效果,都是不可修改【value】和指向
- p2可以修改【value】,不可修改指向
常引用(Const Reference)
- 引用可以被const修饰,这样就无法通过引用修改数据了,可以称为常引用
- const必须写在&符号的左边,才能算是常引用
const引用的特点:
- 可以指向临时数据(常量、表达式、函数返回值等)
- 可以指向不同类型的数据
- 作为函数参数时(此规则也适用于const指针)
- 可以接受const和非const实参(非const引用,只能接受非const实参)
- 可以跟非const引用构成重载
- 当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量
数组的引用
- 常见的2种写法
int arr[3] = {1, 2, 3};
int (&ref1)[3] = arr;
int const *ref2 = arr;
cout << arr << endl;
cout << ref1 << endl;
cout << ref2 << endl;
// log:
0x7ffeefbff4cc
0x7ffeefbff4cc
0x7ffeefbff4cc
引用的本质
- 引用的本质就是指针,只是编译器削弱了它的功能,所以引用就是弱化了的指针
- 一个引用占用一个指针的大小
void test(){
int age = 10;
int *p1 = &age;
int &ref = age;
cout << *p1 << endl;
cout << ref << endl;
cout << p1 << endl;
cout << &ref << endl;
}
// 函数调用栈:
0x1000011e0 <+0>: pushq %rbp
0x1000011e1 <+1>: movq %rsp, %rbp
0x1000011e4 <+4>: subq $0x40, %rsp
0x1000011e8 <+8>: movq 0xe19(%rip), %rdi ; (void *)0x00007fffa56a4770: std::__1::cout
-> 0x1000011ef <+15>: movl $0xa, -0x4(%rbp)
0x1000011f6 <+22>: leaq -0x4(%rbp), %rax
0x1000011fa <+26>: movq %rax, -0x10(%rbp)
0x1000011fe <+30>: movq %rax, -0x18(%rbp)
0x100001202 <+34>: movq -0x10(%rbp), %rax
0x100001206 <+38>: movl (%rax), %esi
...
...
// log:
10
10
0x7ffeefbff4bc
0x7ffeefbff4bc
- 可以看到,<+26>和<+30>都是本质都是一样的:把变量age的地址值赋值给指针/引用