01.c++入门

目录

1. 命名空间

1.1 为什么要用命名空间

 1.2 命名空间的使用

1.3 域的访问【局部域>全局域>展开了的命名空间域("using namespace N;",该域暴露在全局中)or指定访问命名空间】

2.缺省参数

2.1 概念

2.2 两种类型的缺省参数

2.2.1 全缺省参数

 2.2.2 半缺省参数

3.函数重载

3.1 函数重载的概念

3.2 C++支持函数重载的原理(名称修饰)

4.extern "C"

5.引用

5.1 引用特点

5.2 常引用

5.3 引用类型的使用

5.3.1 引用做参数

5.3.2 引用做返回值

5.4 传值与穿引用的效率

5.5 引用与指针的比较

5.5.1 引用和指针的不同点

6.内联函数

6.1 特性

7.c++与c语言


1. 命名空间

1.1 为什么要用命名空间

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,namespace关键字的出现就是针对这种问题的。

//1.普通的命名空间

namespace N1                //N1为命名空间的名称

{

        //命名空间中的内容,既可以定义变量,也可以定义函数

        int a;

        int Add(int left,int right)

        {

                return left+right;

        }

}





//2.命名空间可嵌套

//命名空间嵌套的需求:命名空间内也可能冲突

//命名冲突包括1.库冲突和2.项目之间冲突

namespace N2

{

        int a;

        int b;

        int Add(int left,int right)

        {

                return left+right;

        }

        namespace N3

        {

                int c;

                int d;

                int Sub(int left,int right)

                {

                        return left-right;

                } 

        }

}

//3.同一个工程中,允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中

namespace N1

{

        int Mul(int left,int right)

        {

                return left*right;

        }

}

//需要注意的是一个命名空间相当于定义了一个新的作用域,命名空间内的所有内容都局限在该命名空间中。

 1.2 命名空间的使用

a.加命名空间名称以及域作用限定符使用

int main()
{
    printf("%d\n",N::a);
    return 0;
}

b.使用using将命名空间中的某个成员引入

using N::b;
int main()
{
    printf("%d\n",N::a);
    printf("%d\n",b);
    return 0;
}

c.使用using namespace命名空间名称引入

using namespace N;
int main()
{
    printg("%d\n",N::a);
    printg("%d\n",b);
    Add(10,20);
    return 0;
}

1.3 域的访问【局部域>全局域>展开了的命名空间域("using namespace N;",该域暴露在全局中)or指定访问命名空间】

int a=0;                //全局变量

int main()

{

        int a=1;

        printf("%d\n",a);                //打印的为局部的a

        printf("%d\n",::a);                //打印的为全局的a,其中::为域作用限定符

        return 0;

}

std:c++标准库,其中包含stl与c++库

命名空间的展开表示会不会去这个命名空间搜索,例如不加#include<iostream>等#include<......>,则不不会在#include<iostream>等#include<......>中搜索。

情况一:std::cout<<"hello world"<<std::endl;

情况二:using namespace std;

               cout<<"hello world"<<endl;

其中,<<为流插入运算符,>>为流提取运算符,cout与cin都有自动识别类型的特点,c与c++可混编。

tip:具体做项目时不建议直接像情况二一样全部展开,不然会有风险,如果与库命名冲突则会报错,建议具体项目中不展开(日常练习可以展开),做项目时部分展开,例如using std::cout。

2.缺省参数

2.1 概念

缺省参数是声明或定义函数时为函数的参数确定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。

void Func(int a=0)
{
    cout<<a<<endl;
}

int main()
{
    Func();    //没传参时,使用参数的默认值,即a=0
    Func(1);    //传参时,使用指定的实参,即a=1

    return 0;
}

2.2 两种类型的缺省参数

2.2.1 全缺省参数

void Func(int a=10,int b=20,int c=30)
{
    cout<<a<<endl;
    cout<<b<<endl;
    cout<<c<<endl;
    
}

 2.2.2 半缺省参数

void Func(int a,int b=20,int c=30)
{
    cout<<a<<endl;
    cout<<b<<endl;
    cout<<c<<endl;
    
}

Tip:

1.半缺省参数必须从右往左以此给出,不可以间隔给值。

2.缺省值必须为常量或者全局变量。

3.C语言的编译器不支持缺省参数。

4.缺省参数不能在函数声明和定义中同时出现。

//test.h
void Func(int a=10);    //函数的声明


//test.cpp
void Func(int a=20)    //函数的定义
{}

//如果声明和定义位置同时出现缺省值,且两处值不同,编译器会无法识别到底该用哪个缺省值

3.函数重载

理解:自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,那么就可以认为该词被重载了。

3.1 函数重载的概念

函数重载为函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(1.参数个数 或 2.类型 或 3.类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

3.2 C++支持函数重载的原理(名称修饰)

  1. 名称修饰(Name Mangling):函数重载需要在编译过程中对函数名进行名称修饰,以添加关于参数类型、顺序和个数等信息,从而区分不同的函数。然而,在 C 语言中,没有提供名称修饰机制,函数名在编译后直接映射到可执行代码中,不包含额外的信息。

  2. 兼容性:C 语言是一种面向底层编程的语言,与汇编语言和机器码有较强的兼容性。因此,C 语言的设计目标是保持简单、直接和高效,避免引入复杂的语法和额外的处理逻辑。函数重载会增加编译器的复杂度,不符合 C 语言的初衷。

  3. 编译器设计:C 语言的早期编译器设计主要集中在快速生成高效的汇编代码上,没有将函数重载的需求作为首要考虑。在 C 语言标准制定的时候,函数重载并不是一个普遍存在或必要的特性。

在c/c++中,程序运行起来要经历预处理,编译,汇编,链接四个阶段。

例如:list.h,list.c,test.c(项目一般由多个头文件.h和多个源文件.cpp构成,)

预处理阶段:将头文件展开/宏替换/条件编译/去掉注释。(成为list.i,test.i)

编译阶段:检查语法,生成汇编代码。(成为list.s,test.s)

汇编阶段:将汇编代码转为二进制机器码。(成为list.o,test.o)

链接阶段:将两个目标文件链接到一起。(举个例子:在汇编阶段生成的.o文件,如果test.o中调用一个函数但是在test.o中找不到该函数地址,那么链接器就会到list.o文件中去找)

对于链接时,test.o会使用哪个名字去查找,举一个linux下g++的例子:

用gcc测试(gcc -o testc test.c):函数名修饰不会变。

用g++测试(gcc -o testcpp test.cpp):函数名的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

总结来说,在Linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中,变成"_Z+函数长度+函数名+类型字母"。

综上,c语言不支持函数重载,但是c++支持函数重载。(函数名修饰规则不一样,可能会导致链接失败)

4.extern "C"

因为c和c++编译器对函数名字的修饰规则不一样,故在某些场景下会出现问题

1.c++中调用c的静态库或者动态库,c调用c++的静态库或者动态库。

2.c与c++混编时。

故引入extern"C"来告诉编译器,按照C语言规则来编译。

#ifdef __cplusplus
extern "C" {
#endif

// 在这里放置C风格的函数声明或定义

#ifdef __cplusplus
}
#endif

5.引用

引用不是新定义一个变量,而是给已经定义的变量一个别名,编译器不会额外为其开辟空间,它和它的变量共用同一片空间。

5.1 引用特点

1.引用在定义的时候必须初始化。

2.一个变量可以有多个引用。

3.引用名只可对应一个引用变量,不可对应多个。

5.2 常引用

void TestConstRef()
{
    const int a=10;

    //int& ra=a;    //语句报错,变为ra后权限放大,a具有常属性,不可修改
    const int& ra=a;    //正确

    //int& b=10;    //10为字面量常量,具有常属性,而引用类型b为可修改,故错误
    const int& b=10;    //正确

    double d=11.11;
    //int& rd=d;    //类型不符,错误
    const double& rd=d;    //正确
}

5.3 引用类型的使用

5.3.1 引用做参数

  1. 直接操作原始对象:通过将参数定义为引用,函数可以直接操作原始对象,而无需对对象进行拷贝或创建副本。这样可以提高效率并避免不必要的内存开销。

  2. 改变原始对象的值:通过引用参数,函数可以修改原始对象的值,这样可以实现在函数内部对原始对象进行更改,并使修改在函数外仍然有效。

  3. 传递大型对象的效率:将对象作为引用参数传递,比将对象作为值参数传递更高效。因为不涉及拷贝操作,尤其对于大型对象来说,可以显著提高性能。

  4. 提供多个返回值:函数可以同时返回多个值,将其作为引用参数传递给函数,并在函数内对引用参数进行修改。

5.3.2 引用做返回值

  1. 避免拷贝:使用引用作为返回值,可以避免大型对象的拷贝操作,提高性能和效率。
  2. 内存安全:通过返回引用,可以确保返回值与原始对象共享相同的内存空间,避免了不必要的内存分配和释放。
  3. 支持连续赋值:由于返回的是引用,可以将多个赋值操作连续执行,提高代码的简洁性和可读性。
  4. 可修改原始对象:通过返回引用,可以直接修改原始对象的值,而无需再次传递和修改副本。

需要注意的是,如果函数返回时,出了函数作用域,若返回的对象生命周期还在,则可以使用引用返回,如果生命周期结束了,则会造成悬空引用,此时只可用穿值返回或者将该对象用static静态修饰。

5.4 传值与穿引用的效率

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。

5.5 引用与指针的比较

在底层实现上引用实际是有空间的,因为引用是按照指针方式来实现的。

5.5.1 引用和指针的不同点

1. 引用概念上定义一个变量的别名,指针存储一个变量地址。

2. 引用在定义时必须初始化,指针没有要求。

3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。

4. 没有NULL引用,但有NULL指针。

5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占 4个字节)。

6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。

7. 有多级指针,但是没有多级引用。

8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。

9. 引用比指针使用起来相对更安全。

6.内联函数

以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧 的开销,内联函数提升程序运行的效率。

6.1 特性

1. inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替 换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。

2. inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规 模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、频繁调用的函数 采用inline修饰,否则编译器会忽略inline特性。

3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会 找不到。

question1:宏的优缺点?

优点:

1.增强代码的复用性。

2.提高性能。

缺点:

1.不方便调试宏。(因为预编译阶段进行了替换)

2.导致代码可读性差,可维护性差,容易误用。

3.没有类型安全的检查 。

question2:C++有哪些技术替代宏?

1. 常量定义 换用const enum.

2. 短小函数定义 换用内联函数.

7.c++与c语言

// 这里仅举一个例子

//要求将数据中的值乘2倍,再打印

//1.c语言中中的做法

for(int i=0;i<sizeof(array)/sizeof(int);++i)

{
        array[j]*=2;

}

for(int i=0;i<sizeof(array)/sizeof(int);++i)

{
        cout<<array[i]<<" ";

}

cout<<endl;

//c++实现->范围for(语法糖)->特点:写起来比较简洁

for(auto&e:array)

{
        e*=2;

}

for(auto e:array)

{
        cout<<e<<" ";

}

cout<<endl;

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

chhei.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值