内联函数、引用、const

C++.png

内联函数 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
注意点:
  1. 引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)
  2. 对引用做计算,就是对引用所指向的变量做计算
  3. 在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”
  4. 可以利用引用初始化另一个引用,相当于某个变量的多个别名
  5. 不存在【引用的引用、指向引用的指针、引用数组】

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引用的特点:
  1. 可以指向临时数据(常量、表达式、函数返回值等)
  2. 可以指向不同类型的数据
  3. 作为函数参数时(此规则也适用于const指针)
    1. 可以接受const和非const实参(非const引用,只能接受非const实参)
    2. 可以跟非const引用构成重载
  4. 当常引用指向了不同类型的数据时,会产生临时变量,即引用指向的并不是初始化时的那个变量
数组的引用
  • 常见的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的地址值赋值给指针/引用
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351