【C++】C向C++知识的过度(下)

前言

承接上文C向C++知识的过度(上)感兴趣可以了解一下。

一、C与C++中的&取址符

1.1 C中的&取址符回顾与拓展

普通用法:p指针变量保存了a变量的地址。

int a=10;
int *p=&a;

一维数组与指针的用法:

int arr[3] = {1,2,3};
int* parr = arr;

数组指针用法:如果是数组指针移动+1 移动整个数组的大小。

int(* parr2)[3] = &arr;

函数指针用法:my_func前面的&可有可无。
在这里插入图片描述

1.2 C++中的&取址符

在C++面向对象中,我们多一种函数,叫做成员函数
成员函数指针的调用与成员函数的调用是一样,也是依赖于结构体对象(变量)的调用。

使用成员函数指针,调用成员函数的方法如下:

#include <iostream>
using namespace std;
struct Stu
{
    string name;
    int age;
    void write_code()
    {
        cout << "正在努力地写代码" << endl;
    }
};
int main()
{
    void (Stu::*pfun)() = &Stu::write_code;
    Stu stu;
    (stu.*pfun)();
    return 0;
}

&在成员函数中,取址的方式的用法。指向此函数地址的函数指针。
在这里插入图片描述

二、C与C++中的const修饰符

2.1 C中的const修饰符回顾与拓展

const修饰的变量叫做只读变量。这个变量的值是不可以修改的。
但是它终究还是个变量。

const在C的一些用法:

#include <stdio.h>
int main()
{
    const int a = 10;
    //a = 100;错误的,不能通过只读变量修改变量中的值。

	//在C中const修饰的这种只读变量,是可以通过指针的方式进行修改的。
    int* p = &a;
    *p = 10000;
    printf("%d \n",a);//打印10000
    int arr[a] = {0};
    //不能做为定义数组的长度使用。
    //这种使用只读变量去定义数组的方式是错误的。
    return 0;
}

const关于指针用法回顾:

#include <iostream>
using namespace std;
int main()
{
    int a = 100;
    const int* p = &a; 
    //const修饰是谁? int* 这个指针的类型。
    //指针有三种属性:读操作,写操作,移动
    //*p = 1000;//*操作就没有写的属性,所以不可以通过*的方式向内存写数据。
    int c = 500;
    p = &c;//这种方法是可以实现的
    
    int const* p1 = &a; //修饰的int*,与上面的效果是一样的。
    //*p1 = 1000;这是错误的。
    
    int d = 1000;
    int* const p2 = &d;//此时const修饰的是p2,p2是指针变量。
    *p2 = 5000;//p2中保存这个地址是不可以改变。
    //p2 = &a;//这是不对的。            
    return 0;
}

2.2 C++中const的用法

#define MAX 1024

官方推荐 当定义宏时在C++中推荐const修饰的方式来替换宏定义。

const int my_max = 1024;

使用这种方式更加安全可靠。
因为C++是面向大型程序的,所以要更严谨一些。

C++中的const修饰符:

#include <iostream>
using namespace std;

int main()
{
    const int a=10;
    int *p=(int*)&a;
    *p=60;
    cout << a << endl;
    cout << *p << endl;
    return 0;
}

在这里插入图片描述
为什么在C++中a还是10,并没有被改变呢?
这与我们的C++编译器优化有关。

这个a的10,在再次使用的时候,编译器没有从这个地址中去取值。
当const修饰这个变量如果是一个立即数时,编译器会把这个值直接放在寄存器中,在程序中如果出现a这个变量时,编译器直接把a以一个立即数的方式直接替换。我们的编译器是直接从寄存器上取的值。

我们把这个const修饰的变量承接是立即数的变量,也叫做符号常量。他在使用时,编译器是直接在寄存器中取这个值。

意义:程序的运行的高效性。

如果想要获取a中的值的话,就让编译器老老实实去a的址中去取址,使用volatile进行修饰,或者以下面这种方式

int b=10;
const int a=b;//只读变量

当const修饰的变量承接的立即数时,就可以完全视之为一个常量使用。

三、C++中内联函数

C++内联函数就是用来提高代码运行效率的,主要用于继承C中宏函数的高效的特性,同时又避免宏函数所出现的莫名的Bug
但也是有代价的,因为他是直接展开,所以最终会造成可执行程序的代码的膨胀。

3.1 C中宏函数

以下MAX(a,b)就是一个宏函数

#include <stdio.h>
#define MAX(a,b) a>b?a:b
int main()
{
    int x=10;
    int y=20;
    printf("%d",MAX(x,y));
    return 0;
}

3.2 C++中的内联函数 inline

函数定义前加inline,就是说建议编译器把这个函数进行内联。
内联函数既保障了代码运行的高效性,同时也避免了宏函数Bug的存在。
但是内联函数是有代价的:
只能使用代码简短的,且没有循环,没有耗时操作的函数才可以内联,内联函数不可以有递归。
内联函数的代价:最终可执行代码的膨胀。

普通函数的调用过程:
调用完之后,就不存在了,所以最终的可执行程序的代码量不会膨胀。

#include <iostream>
using namespace std;

inline int max(int a,int b)
{
    return a>b?a:b;
}
int main()
{
    int a=10;
    int b=20;
    cout << max(a,b) << endl;
    return 0;
}

3.3 C++中内联函数和带参宏有什么区别?

  1. 宏定义的替换是在预处理阶段完成的,内联函数是在编译阶段完成的;
  2. 内联函数也是函数,和函数的使用方式一样,也会做类型的检查,而宏定义只是能完成简单的替换。

四、C++中函数的默认参数

C++函数的参数是可以设定默认值,设定默认值的方式被认为是C++函数使用的一种灵活性的体现。

  1. C语言中函数的形参的值必须由实参传递过来,而C++中可以给函数的形参一个默认值,
    如果有默认值了,实参传了,就用实参的值。
    实参没传,就用默认值。
  2. 因为函数传参遵循的靠左原则,所以给定默认参数时要遵循靠右原则,否则有歧义,会报错。
    函数参数的入栈过程是依次从右往左进行的,所以函数的默认参数在设定时也要从右往左依次设定,不能跳跃。
  3. 默认参数只能写在函数声明处,不能写在定义处。因为函数调用时,优先看到的是声明。
#include <iostream>
using namespace std;

int add(int a,int b,int c=10)
{
    return a+b+c;
}
int main()
{
    int a=10;
    int b=20;
    cout << add(a,b) << endl;//输出40
    return 0;
}

结果展示:
在这里插入图片描述

五、C++中函数重载机制

第一种静态多态的机制
多态:即同名函数实现的不同的形式(执行不同的逻辑)
在C中是没有函数重载,当函数逻辑类似,但参数不同时就得不断的去找一些合适的函数名去定义。
函数重载,支持定义重名的函数。

C++函数重载:是根制函数参数中的参数类型,参数顺序,参数个数的不同,调用与之匹配的函数。
与返回值是没有关系。
重载函数的调用会根据类型的不同,自动推导该调用那个版本的函数。

编译器在编译的过程中,其实已经根据类型不同生成了不同名字的函数,只不过在C++层面上看到的名字是相同的。

#include <iostream>
using namespace std;

int add(int a,int b,int c)
{
    return a+b+c;
}
int add(int a,int b)
{
    return a+b;
}
int main()
{
    int a=10;
    int b=20;
    int c=30;
    cout << add(a,b) << endl;//结果是30
    cout << add(a,b,c) << endl;//结果是60
    return 0;
}

结果展示:
在这里插入图片描述

注:
一定要注意不要出现下面的用法,有歧义会报错

#include <iostream>
using namespace std;

int add(int a,int b,int c=10)
{
    return a+b+c;
}
int add(int a,int b)
{
    return a+b;
}
int main()
{
    int a=10;
    int b=20;
    int c=30;
    //cout << add(a,b) << endl;
    //有歧义会报错
    return 0;
}

六、哑元

在定义函数的时候,某个或某几个参数只有类型,没有参数名这个类型只起到一个占位的作用。
哑元一般在代码升级的过程中可能会用到。
如原本函数的实现需要3个参数,升级后发现只有两个就够了,此时只需要修改函数的定义,将不用的那个参数改成哑元就无须修改函数调用处了。
注意:虽然支持,但是一般不这样使用。
哑元的必须使用处:自增、自减运算符重载的时候,会用到哑元占位。

#include <iostream>
using namespace std;

int add(int a,int,int c=10)
{
    return a+c;
}

int main()
{
    int a=10;
    int b=20;
    int c=30;
    cout << add(a,b,c) << endl;//输出40
    return 0;
}

结果展示:
在这里插入图片描述

评论 56
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

夜猫徐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值