初学c++

本文介绍了C++中的命名空间,用于解决命名冲突问题,以及全展开和部分展开的使用策略。接着讨论了引用的概念,包括作为返回值和参数传递的情况,强调了常引用的重要性。此外,文章还涵盖了函数重载、缺省参数、内联函数的原理和注意事项,以及nullptr的使用,以增强代码的可读性和安全性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

命名空间

展开命名空间(全展开)

部分展开

缺省参数

全缺省

半缺省

函数重载

引用

 引用做返回值

传值传参

  传引用返回

常引用

内联函数

代码膨胀

auto关键字

nullptr


命名空间

c语言有着多方命名冲突,会大量冲突的问题,官方库,第三方库都会产生冲突,所以在c++就有了命名空间

namespace 关键字

用来创建命名空间,一个命名空间定义了一个新的作用域

命名空间可以定义变量,函数,类型,命名空间

不同文件相同命名空间名会自动合并,并不会冲突

:: 域作用限定符

访问命名空间的成员

namespace jj//(命名空间名)
{
   
    int num=1;
    int add(int x,int y)
    {
        return x+y;
	}
    struct ST
    {
        int* a;
        int top;
        int capacity;
	};
}
int main()
{
    //使用方法,域作用限定符
    printf("%d\n",jj::num);
    printf("%d\n",jj::add(1,2));
    //结构体的限定符加在struct后
    struct jj::ST St;
    //
    return 0;
}

展开命名空间(全展开)

using namespace jj;

int num=0;
namespace jj//(命名空间名)
{
    //命名空间可以定义变量,函数,类型
    int num=1;
    int add(int x,int y)
    {
        return x+y;
	}
    struct ST
    {
        int* a;
        int top;
        int capacity;
	};
}
//展开命名空间(全展开)
using namespace jj;
int main()
{
   	printf("%d\n",num);
    return 0;
}

 

 展开虽然可以使用命名空间的成员,但是有危险,当有重命时编译器会无法判断到底使用哪里的,所以需要灵活使用

部分展开

using jj::num

namespace jj
{

    int num=1;
    //套娃
    namespace kk
    {
        int num=3;
    }
}
//部分展开
using jj::num;
int main()
{
    printf("%d\n",num);
    printf("%d\n",jj::kk::num);
    return 0;
}

 

展开std标准库(c++标准库的命名空间)

头文件 iostream

using namespace std;

#include<iostream>
//全展
using namespace std;

cout

控制台输出,自动识别类型

cin

标准输入,自动识别类型

endl

换行符

<<

流插入运算符

>>

流提取运算

#include<iostream>
using std::endl;
using std::cin;
using std::cout;
int main()
{
    int i=10;
    cout<<i<<endl;
    cin>>i;
    cout<<i<<endl;  
}

缺省参数

void Func(int n=1)
{
    cout<<n<<endl;
}
int main()
{
    Func(0);
    //如果没有传参会使用缺省参数,可以认为是"备胎"
    Func();
    return 0;
}

全缺省

缺省参数传参时不能只传一两个,不传完,还得是从左向右传参,要么传要么就不传

void Func(int x=1,int y=2,int z=3)
{
   	 cout<<x<<endl;
     cout<<y<<endl;
     cout<<z<<endl;
}
int main()
{
    Func();
    return 0;
}

半缺省

必须从右向左,给缺省值

void Func(int x,int y=2,int z=3)
{
   	 cout<<x<<endl;
     cout<<y<<endl;
     cout<<z<<endl;
}
int main()
{
     //这种就是错的
    Func(3,,5);
    //以下正确
	Func(6);
    Func(6,5);
    Func(6,5,4);
    return 0;
}

如果声明和定义分离同事给缺省参数,规定分离时声明给定义不给

函数重载

允许一个函数有多个功能

c语言不允许同名函数

函数名相同,同一个作用域,参数不同,顺序不同,类型不同,个数不同,才能构成函数重载

返回值不同不能构成重载,不知道调用哪个

为什么c语言不支持重载?

c语言编译过程

预处理

头文件展开,宏替换和删除,去注释条件编译

test.i(通过预处理后产生.i的中间文件)

编译,检查语法,生成汇编代码

test.s(汇编文件,由预处理得到的源代码文件)

汇编,将汇编代码生成二进制机器码,生成符号表(函数名与地址的映射)

test.o(目标文件)

链接,生成可执行程序

c语言在进行汇编时会直接用函数名当做符号表的名字,一旦有重复的函数名出现在符号表,就会导致混乱无法区分,所以c语言无法函数重载

但是c++有着函数名修饰规则

函数名修饰规则不同编译器有不同的修饰规则

如在linux中两个函数重载的函数名func在符号表中是这样的

void func(int i,double d)
void func(double d,int i)
void func(int i,double d)
void func(double d,int i)

_Z4funcid

_Z4funcd

 

引用

取别名

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

结果是2,2.

这段代码中b是a的别名,b和a是同一个地址,改变b的值,a也会改变

根据这个特性可以实现指针的传址调用同样的效果

void Swap(int&a,int&b)
{
    int c=a;
    a=b;
    b=c;
}
int main()
{
    int x=1,y=0;
    Swap(x,y);
    cout<<x<<endl<<y<<endl;
    return 0;
}

结果是0,1

&只有在类型和类型名之间才是取别名的意思

引用类型必须在定义的地方初始化,且类型必须相同

一个变量可以有多个引用

引用一个实体后,不能再引用其他实体,不能重定义

引用相较于传值调用效率较高

 引用做返回值

int func()
{
    int n=1;
    return n;
}
int main()
{
    int ret =func();
    return 0;
}

这段代码并不是func直接返回n的值赋值给ret,在返回n之前func函数已经销毁了

所以在值小的时候会把值传给寄存器,再由寄存器来传给ret

当值过大时会在栈帧里开辟一片空间做返回值

这里统称为临时变量

传值传参

void func(int& x)
{
    x++;
}
int main()
{
    int a=0;
    func(a);
    return 0;
}

传值传参可以提高效率

输出型参数(修改形参会改变实参)

  传引用返回

int& func()
{
    int n=1;
    return n;
}
int main()
{
    int ret =func();
    return 0;
}

这段代码返回了n的别名,但是同样的在返回之前栈帧已经销毁(还给系统)了,当这个值返回后ret可能为1,但也有可能为随机值,因为n的那片空间已经销毁,1可能存在也可能不存在

所以使用引用反回时,出了函数作用域,返回对象还在没有被销毁,那就可以使用引用返回,如果被销毁,那就必须使用传值调用

传引用返回同样可以提升效率

修改返回对象

int& func(int*& a)
{
    a[0] = 0;
    a[1] = 1;
    a[2] = 0;
    return a[0];
}
int main()
{

    int* a = (int*)malloc(sizeof(int) * 3);
    
    func(a)++;
    cout << a[0] << endl;
    cout << a[1] << endl;
    cout << a[2] << endl;
    free(a);
    return 0;
}

结果是1,1,0

常引用

int func()
{
    int i=1;
    return i;
}
int main()
{
    //1
    const int a=0;
	int& b=a;
    //2
    const int&c=a;
    //3
    int d=0;
    const int& e=d;
    //4
    int f=0;
    double& g=f;
    //5
    int& h=func();
}

权限放大

1.中a被修饰,a的值无法改变,但是给a取了个别名b,b却没有const的修饰,这就导致了b可以重新赋值,这两条代码无法执行

权限平移

2.中a又取了个别名c,c也被const给修饰了,c无法改变和a一样,这条语句可以执行

权限缩小

3.中给d取了别名e,但是e却被修饰,无法改变值,这两条语句可以执行

在引用中权限可以相等,缩小,但是不能放大

4.中给f取了个别名g,赋值会产生一个临时变量,临时变量具有常属性,把一个常量给一个变量,导致了权限的放大,所以用const修饰下g,权限平移这条语句就可以过了

5.中的问题是返回i的值时也是创建了临时变量,把一个常量赋给变量h,涉及了权限的放大

 

引用和指针的区别

引用语法上定义了一个变量的别名,指针是存储的变量地址

引用在定义时必须初始化,指针不需要

引用在定义一个别名后不能改变,在成为其他实体的别名

没有空引用,有空指针

sizoef计算引用时是引用类型的大小,计算指针时始终是地址空间字节数(64位占8字节,32位占4字节)

引用++是原名值加1,指针++是指针向后偏移一个类型的大小

有多级指针,没有多级引用

访问原数据不一样,指针需要解引用,引用靠编译器处理

引用相对指针更安全

int main()
{
    int a=0;
    int *b=&a;
    int&c=a;
    return 0;
}

 

  从底层上看引用和指针是一样的

内联函数

在c语言中有着#define宏定义关键字

优点:增强代码复用性,提高性能

缺点: 坑多,可读性差,不方便调试,没有类型安全检查

在c++中就出现了内联函数关键字,替代了宏替换

inline关键字

内联函数的优势不用建立栈帧

将函数定义成内联函数

在被调用的地方展开函数(不用调用函数就使用函数的功能)

inline int add(int* x,int* y)
{
    return x+y;
}
int main()
{
    int x=1,y=1;
  	int z = add(x, y);
    printf("%d\n", z);
    return 0;
}

 

在汇编代码中可以看到并没有调用add函数

inline对于编译器而言是一个建议,编译器要判断函数的规模小(语句不长),不是递归,频繁使用,编译器才会执行,不然编译器可以忽略

代码膨胀

inline int add(int x, int y)
{
    x++;
    x++;
    x++;
    x++;
    x++;
    x++;
    x++;
    x++;
    x++;
    x++;
    x++;
    x++;
    x++;
    x++;
    return x + y;
}
int main()
{
    int x = 1, y = 1;
    int z = add(x, y);
    printf("%d\n", z);
    return 0;
}

 

从图中可以发现由于函数语句过长,导致编译器忽略了内联函数

内联函数不能声明定义分离(分离在两个文件)会出现链接错误

因为在调用内联函数有只有声明包含(.h)无定义函数无法展开,只能去加载函数找地址又因为内联函数无法进入符号表,导致出错。

直接定义在.h文件即可解决

代码膨胀的后果程序变大

auto关键字

自动推导类型

int main()
{
    int a=0;
    auto b=a;
    double f=3.14;
    auto g=f;
    return 0;
}

这种情况下auto没什么用处的

但是当类型很长的情况下,就非常有用了

如下:

int main()
{
    int a=0;
    auto b=a;
    double f=3.14;
    auto g=f;
    return 0;
}

auto不能当做参数,也不能定义数组

 

nullptr

c++中NULL定义为了零 会遇到不可避免的错误

void func(int a)
{
    cout << "int" << endl;
}
void func(int* a)
{
    cout << "int*" << endl;
}
int main()
{
    func(0);
    func(NULL);
    func(nullptr);
    return 0;
}
//结果是int,int,int*

在重载函数中NULL被当做0使用

在c++中就nullptr

nullptr在c++中是作为关键字引入的

sizeof(nullptr)与sizeof((void*)0)的字节数相同

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值