C++相关基础概念之入门讲解(下)

1. 引用

​
int main()
{
     const int a=10;
    int& aa=a;
    aa++;
    cout<<aa<<endl;
}
引用不是新定义一个变量,而给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间(初期这么理解就好,实际上在底层是开了空间的)
引用的使用方法就是:类型& 引用变量名(对象名) = 引用实体;
举个例子:

在这个代码里面aa是a的别名,aa++了,也就是a++;所以这里的输出是11;

举个例子,现在有一个人叫小明,我们给他取一个别名叫明明,明明去吃饭了,那是不是小明也去吃饭了,引用就是这个意思。

注意:引用类型必须和引用实体同种类型。也就是说在上面这个代码里面,aa要和a是同一个类型(int)。

1.1 常引用

​int main()
{
    const int a=10;
    int& aa=a;
    aa++;
    cout<<aa<<endl;
}

在这个代码里面,如果a是const int类型的,那就不可以对aa进行++的操作(会报错)。

举个例子,小明不可以吃饭,那也就是说明明不可以吃饭。

1.2 传值返回与传引用返回

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直
接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效
率是非常低下的,尤其是当参数或者返回值类型非常大时(比如说map<string,string>),效率就更低。
传值返回:
int Fanc(int a)
{
    a++;
    return a;
}

int main()
{
    int b=1;
    b=Fanc(b);
    cout<<b<<endl;
    return 0;
}

在这段代码里面,最终的结果是2。但是他在return a的时候产生了一份临时拷贝,然后把这个临时拷贝赋值给了b。我们前面说的消耗就是在这里。

PS:之所以要拷贝的原因涉及到函数栈帧的创建与销毁,简单来说就是a出了这个Fanc就会自动销毁,所以编译器要通过产生临时拷贝的方式来进行赋值。

传引用返回:

​int& Fanc(int a)
{
    a++;
    return a;
}

int main()
{
    int b=1;
    b=Fanc(b);
    cout<<b<<endl;
    return 0;
}

在这段代码里面,a其实是有危险的,因为是&,所以在这里并没用产生a的临时拷贝,也就是说在这里实际上造成了野指针现象。

修改的办法有两种。

一种是加一个全局变量

int c;//  全局变量

int& Fanc(int a) {
    c = a + 1;  
    return c;   
}

int main() {
    int b = 1;
    b = Fanc(b);
    cout << b << endl; 
    return 0;
}

传入的是全局变量(或者静态变量也可以),所以在这里并不会被销毁。

还有一种是通过引用传递参数

int& Fanc(int& a) {
    a++;        // 直接修改传入的参数
    return a;   // 返回传入参数的引用
}

int main() {
    int b = 1;
    Fanc(b); 
    cout << b << endl;  
    return 0;
}
通过引用传递参数,直接修改传入的参数并返回其引用

1.3 传值、传引用

首先,传值和传引用与传值返回与传引用返回不是同一个东西。

传值:

​​int Fanc(int a)
{
    a++;
    return a;
}

int main()
{
    int b=1;
    b=Fanc(b);
    cout<<b<<endl;
    return 0;
}

这个就是我们在一开始的学习中使用函数的方式。消耗也比较大。

传引用:

​
​int Fanc(int& a)
{
    a++;
    return a;
}

int main()
{
    int b=1;
    Fanc(b);//没有了b=...
    cout<<b<<endl;
    return 0;
}

​

直接修改传入的参数,而不需要返回值。同时代价也小。

1.4 引用和指针的区别

在C++中,引用和指针都是用来间接访问变量的机制,但它们之间有一些重要的区别:

1. 引用是变量的别名,而指针是一个独立的实体。引用在声明时必须初始化,并且一旦引用和原变量绑定后,就无法再绑定到其他变量(这一点很重要);而指针可以在声明后指向不同的变量。

2. 引用不需要使用解引用操作符(*)来访问其绑定的变量,而指针需要使用解引用操作符(*)来访问其指向的变量。

3. 引用不能指向空值(null),而指针可以指向空值。

4. 指针可以进行算术运算,而引用不支持。

总的来说,引用更直观和安全,因为它不需要对空值进行处理,而指针更灵活,因为它可以指向不同的对象和进行算术运算。在实际使用中,应根据具体情况选择合适的机制。

2. 内联函数

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调
用建立栈帧的开销,内联函数提升程序运行的效率。
inline int add(int a, int b)
{
    return a + b;
}

int main() 
{
    int result = add(3, 4);  // 编译器可能会将 add 函数直接插入此处
    cout << "Result: " << result << endl;
    return 0;
}

内联函数的本质就是替换,通过把main函数里面的add直接转换成a+b,通过这样的方式来提升效率。

2.1 内联函数特性

inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会
用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
行效率。
inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建
议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)
是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性(不然的话会造成代码膨胀)。
PS:inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址
了,链接就会找不到。
内联函数从某种意义上来说替代了宏(即宏函数)。

3. auto关键字

auto我从刚刚学到他的时候认为没什么用,但是当我学到后面的时候,我才发现他是那么的好用,因为到后面很多时候类型是一层套一层,就是说会特别的长,这个时候auto的作用就体现出来了。

他的本质就是让编译器自动推导类型。换种说法就是把我们的这部分工作交给了编译器。

3.1 auto使用时候的注意事项

使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto
的实际类型。因此auto并非是一种类型的声明,而是一个类型声明时的占位符,编译器在编
译期会将auto替换为变量实际的类型
int main()
{
    auto x = 10,y=3;
    return 0
}

上面这种就是合法的,因为auto在同一行只会进行一次推导,如果说y=3.3,那么编译器就会报错。

3.2 auto不能推导的类型

auto不可以作为函数的参数类型,比如说:

auto fanc(auto a)
{
    .......
}

这种是不合法的(其实没有什么别的原因,就是设计者在设计的时候没有允许这种用法,我们在后面会学到一种template<class T>的东西,他可以实现我们上面想要达到的目的)。

同时,auto也不可以作为数组的类型,简单来说就是:

int main()
{
    auto arr[]={1,2,3,4,5,6}
    return 0;
}

这种也是不可以的(原因如上)。

4. 范围for

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误(如越界访问)。因 此C++11中引入了基于范围的for循环。for循环后的括号由冒号分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围
void TestFor()
{
    int arr[] = { 1, 2, 3, 4, 5 };
    for(auto& a : arr)
    a *= 2;
}

这就是范围for的用法,通过这样的方式就可以使arr里面的数都*上2。

PS:加上引用就可以改变arr,如果说是想要改变范围for里面的数组,那最好就是在auto后面加一个&。

4.1 范围for的原理

范围for的底层就是迭代器(iterator),他在编译器编译的时候就会修改为迭代器。

所以说我们在里面想要遍历或者修改的东西必须有beginend的方法,beginend就是for循环迭代的范围。

5. 空指针nullptr

程序本意是想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,因此与程序的
初衷相悖。 因此发明了nullptr,
PS:在使用nullptr表示指针空值时,不需要包含头文件。
因此我们在使用的时间候直接把他当做一个不能表示0的NULL去使用就好。

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啊吧怪不啊吧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值