【C++】C++基础知识

目录

C++关键字

命名空间

为什么要使用?

如何定义一个命名空间?

如何使用?

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

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

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

C++输入与输出

缺省参数

全缺省参数

半缺省参数

注意事项

应用实例

函数重载

为什么要有函数重载?

为什么是函数重载?

引用

概念

特性

使用场景

引用和指针的不同?

内联函数

概念

特性

auto关键字

使用细则

不能用的场景

基于范围的for循环

指针空值---nullptr


C++关键字

C++一共有63个关键字,这里先全部列举出来,不做详解。

命名空间

为什么要使用?

一个程序的开发往往由多人合作完成,在此过程中,函数名、变量名、类名是大量出现的,这就很有可能出现命名重复的情况。那么为了避免命名冲突,C++使用命名空间对标识符的名称进行本地化。

这里概括下命名空间的好处:

  1. 避免命名冲突。一个命名空间就定义了一个新的作用域,里面的内容被局限在这个域中,从而与其他模块or库中的同名实体隔离开,避免了命名冲突。

  2. 组织和管理代码。相关的实体被放置在同一个命名空间中,可以更好地组织和管理代码,代码结构清晰可见。

  3. 提供代码封装:命名空间提供了代码的封装性,使得实体的访问范围更加清晰、可控。

如何定义一个命名空间?

namespace关键字+命名空间的名字+{ }

  • { }中为命名空间的成员,可以是变量、函数、自定义类型、命名空间(命名空间可以嵌套)

  • 同一个工程中允许存在相同名称的命名空间,编译器会把名字相同的命名空间合到一起。

  • 同一个命名空间域中不能定义同名的,不然会冲突。不同命名空间域可以。

如何使用?

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

    #include<stdio.h>
    namespace A
    {
       int a=0;
       int b=0;
    }
    int main()
    {
       printf("%d",A::a);
       return 0;
    }

    当你使用某个命名空间的变量时,编译器不会默认去那块空间找,必须你指定变量的命名空间。

    格式:命名空间名+ :: +成员名称

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

    如果某个成员是常用的,那每用一次都要带上命名空间,就会很麻烦。这时,可以把常用的成员引入进来,这样就可以直接使用了。

    注意看这个例子是如何引入a和b的:

    #include<stdio.h>
    namespace A
    {
        int a=0;
        int b=0;
    }
    using A::a;
    int main()
    {
       printf("%d %d",a,A::b);
       return 0;
    }

    格式:using+命名空间名+::+成员名称

    这种方式相当于把a单独展开在全局域中,a可以在其他任何地方被使用。这种方式既能展开常用的成员,又不会暴露整个命名空间。

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

    如果你在使用命名空间A中的任何变量时,都不想加命名空间名称的话,可以把整个命名空间都展开,这样其中的任何成员你都能直接使用了。

    #include<stdio.h>
    namespace A
    {
      int a=0;
      int b=0;
    }
    using namespace A;
    int main()
    {
       printf("%d %d",a,b);
       return 0;
    }

    格式:using namespace +命名空间名称

    引入命名空间A后,在使用a的地方,编译器会先在函数的局部域中找,然后去全局域找,没找到的话再去展开的命名空间域A中寻找a。

    我们看到的“using namespace std; ”,表示在程序中引用了 C++ 标准库中的命名空间 std。这样我们在使用标准库中的各种类和函数时,就不用在前面加上 std::。 如: cin, cout可以直接使用而不需要std::cin, std::cout。

    注意:using namespace引入命名空间的方式虽然方便,但也使命名空间失去了它的封装和保护作用,你定义的内容暴露了出来,就会有重复命名的风险。所以,我们一般只在练习中使用,写项目时,还是尽量使用前两种方法。

C++输入与输出

C++版的hello world:

#include<iostream>
using namespace std;
int main()
{
  cout<<"hello world"<<endl;
  return 0;
}
  • 对应于C语言的printf和scanf,C++使用cout标准输出 和cin标准输入。后者要更加方便,因为不需要再指定格式了,cout和cin,可以自动识别变量类型。'

    C:

    int a;

    scanf("%d",&a); //得指定格式

    C++:

    cin>>a; //自动识别a的类型,更方便

  • endl是特殊的C++符号,表示换行。cout、cin和endl都被包在<iostream>中,所以使用时必须包含<iostream>头文件,并使用标准库的命名空间std。

    补充了解:头文件<iostream>和命名空间std之间,有什么关系?为什么两者都要写进程序?

    <iostream>是C++标准库中的头文件,用于输入输出操作。而std是C++标准库中定义的命名空间,包含了很多与输入输出相关的类和函数。

    在C++程序中,我们可以通过包含头文件<iostream>来引入输入输出的功能,例如输入流std::cin和输出流std::cout。这些输入输出对象都位于std命名空间中,所以在使用它们之前,我们需要使用using namespace std;语句或者在每个对象前加上std::前缀来指明它们所属的命名空间。

    因此,<iostream>std之间的关系是,<iostream>头文件提供了输入输出功能的声明,而std命名空间则包含了具体的实现。通过这种方式,我们可以使用C++标准库提供的输入输出功能。

  • <<是流插入运算符,>>是流提取。

  • C++的头文件不带.h

  • 如何展开使用std? 在日常练习中,直接用using namespace std; 。在代码多的项目中,只展开常用的那部分。如:using std::cout;

缺省参数

缺省参数是C语言所没有的,C++新增的语法功能。我们在声明or定义函数时,为函数的参数指定一个缺省值。在调用该函数时,如果相应位置没有传实参,那么就采用该形参的缺省值,否则使用指定的实参。

缺省值就像备胎。当有实参传过来时,用不上它;当没有实参传过来时,才会用它。

全缺省参数

函数的全部参数都给出了缺省值。

#include<iostream>
using namespace std;
void Func(int a=0,int b=0,int c=0)
{
   cout<<a;
   cout<<b;
   cout<<c;
}
int main()
{
   Func();           //0 0 0
   Func(10);         //10 0 0
   Func(10,20);      //10 20 0
   Func(10,20,30);   //10 20 30
   Func(10,,30);     //不能这么写!要从左往右依次给出
   return 0;
}

半缺省参数

仅部分参数给出了缺省值。

#include<iostream>
using namespace std;
void Func(int a,int b=0,int c=0)
{
   cout<<a<<b<<c;
}
int main()
{
   Func();     //0 0 0
   Func(10);   //10 0 0
   return 0;
}

注意事项

注意:

  • 半缺省参数必须从右往左连续给出,不能间隔着给。

void Func(int a=10,int b,int c){}        //×
void Func(int a=10,int b,int c=30){}    //×
void Func(int a,int b=20,int c=30){}    //√
  • 缺省参数不能在函数定义和声明中同时出现。因为如果两边给出的缺省值不一样,那编译器不知道该采取哪个。我们一般就写在声明中,定义中不写。

  • 缺省值必须是常量or全局变量。

  • C语言不支持。

应用实例

我通过一个实例来展现缺省参数的好处:

//原本给Stack初始化:
void StackInit(Stack*ps){
   int newCapacity=ps->capacity==0?4:2*(ps->capacity);
   ps->a=(int*)malloc(newsCapacity*sizeof(int));
   ……
}    
     //思路是:如果原本容量为0,那么第一次就开4个单位的空间,否则就扩容为之前的两倍
     //但,假如我们初始化时就要开400个空间,那要从开4个空间开始,两倍两倍地慢慢扩容,直至400个。这种方式会不断调用函数、建立栈帧,大大降低了运行效率。
引入缺省参数:
void StackInit(Stack*ps,int capacity=4){
   ps->a=(int*)malloc(capacity*sizeof(int));
}
    //思路:当我不传参,函数会默认开4个空间。当我初始化时想开400个空间,那直接传400即可。

可见,使用缺省参数可以简化代码,提高代码的可读性。

这里罗列了缺省参数的优点:

  1. 简化函数调用:使用缺省参数,函数调用时可以省略一些常用的参数,使函数调用更简洁明了。

  2. 增加函数的灵活性:在函数定义中使用缺省参数可以使函数更加灵活。调用者可以选择性地提供不同的参数值,根据实际需求对函数进行定制。

  3. 向后兼容性:在已经使用该函数的代码中,如果新增了一个参数并且为其设置了默认值,不影响之前的函数调用。因此,使用缺省参数可以确保向后兼容性,避免因为新增参数导致调用错误。

函数重载

为什么要有函数重载?

C语言中,如果功能相似但是形参类型不同,那就得写不同的函数来实现:

int Add1(int a,int b);
double Add2(double a,double b);
double Add3(int a,double b);

这些功能很相似的函数却要取不同的名字来让编译器区分。程序员在调用时,可能也会眼晕,相似的函数名会带来不便。C++对此做出了改进,让功能相似的函数名称统一为一个,这样使用时就很方便,像在用同一个函数一样(但实际上构成重载的函数是不同的),也减轻了记忆函数名的负担。

用函数重载:

int Add(int a,int b);
double Add(double a,double b);
double Add(int a,double b);

为什么是函数重载?

同一个作用域内,多个函数名相同,但形参列表不同(参数个数or参数类型or类型顺序不同)的函数,我们叫做函数重载。常用于处理功能类似但数据类型不同的问题。

注:

  • 返回值不同不构成重载。函数重载是通过形参列表区分的,和其他无关。

  • main函数不能重载,因为程序的入口只能有一个。

  • C语言没有函数重载。

  • 实现函数重载后,我们调用函数传不同的参数时,编译器会根据我们传递实参的类型和个数,判断要调用哪个ADD函数。对调用者来说,就方便多了。

引用

概念

生活中,我们会有朋友取一些绰号,本名也好,绰号也好,实际指的都是同一个人。引用就像是变量的“绰号”,它不是新定义了一个变量,而是给已存在变量取了一个别名,它并未开辟新的内存空间,它和它引用的对象共用同一块内存空间

格式:类型 & 引用变量名(对象名) = 引用实体

例:

int a=0;

int&b=a; //b实际上就是a那块空间。a是int 类型,所以b也是int类型

//这里只开了一块空间a(又名b)

区别于这个例子:

int a=0;

int b=a; //这里开了两块空间,分别叫a和b,并且把a的值赋给了b

特性

  • 引用在定义时必须初始化。

  • 一个变量可以有多个引用。

  • 引用一旦引用一个实体,就不能再引用其他实体。

    int a=0;
    int&b=a;
    int c=0;
    b=c;    //×  b已经引用a那块空间了,不可以再更改

使用场景

  1. 做参数

    • 输出型参数

      如,我们用c语言写交换函数:

      void Swap(int*a,int*b){
        int tmp=*a;
        *a=*b;
        *b=tmp;
      }

      用引用写,直接操纵那片空间:

      void Swap(int& a,int& b){
        int c=a;
        a=b;
        b=c;
      }
    • 大对象传参,提高效率

      比如:实参占4万个字节,如果用形参拷贝的方式,得另开空间。而用引用则可以节省空间,并提高效率。

  2. 做返回值

    注:不是所有情况下都能用引用做返回值!

         引用做返回值的前提:返回对象在出了函数作用域,依然继续存在。如:静态变量

对比这两组:

错误示例:

int& Count()
{
   int n=0;
   n++;
   return n;         //×,出了作用域,n就销毁了,那么一定不能用引用返回,得传值
}
int main()
{
   int ret=Count();
}     

正确示例:

int& Count()
{
   static int n=0;
   n++;
   return n;     //√   出作用域,n未销毁,可以传引用
}
int main()
{
int ret=COunt();  
}

引用和指针的不同?

  • 引用在概念上定义一个变量的别名,指针则是存储一个变量的地址。

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

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

  • 没有NULL引用,但有NULL指针。

  • 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是4或8个字节。

  • 引用自加即引用的实体+1,而指针则是向后偏移一个类型的大小。

  • 有多级指针,但没有多级引用。

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

  • 引用比指针用起来更安全。

内联函数

当短小的函数被频繁调用,如快速排序中频繁地调用Swap函数达十万次,在这期间不断地创建、销毁栈帧,这就导致了效率变低。在C语言中,针对这种情况,我们会用宏函数来写;在C++中,我们用更优的内联函数来处理。

概念

以inline修饰的函数叫内联函数。编译时会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,能够提升效率。

特性

  1. 是种以时间换空间的做法。若编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用。

    注:较长的函数是不会展开的,不然占很多内存。

  2. inline于编译器而言只是个建议,编译器未必会采纳。一般将函数规模较小、不是递归且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。(编译器会自动优化)

  3. inline时,不要将声明和定义分离,分离会导致链接错误。因为内联函数被展开,编译时无法在符号表上保存其有效地址,链接时就不能根据符号表上的地址找到函数,所以会链接不到。所以,内联函数的定义就直接写进.h文件。

auto关键字

当类型名很长时,我们用auto替代。auto可以自动判断变量类型。

注:使用auto时,必须要初始化。因为编译器会在编译阶段根据初始化表达式来推导auto的类型,且编译器会在编译阶段将auto替换为变量实际的类型。

使用细则

  • auto与指针、引用结合起来用时

    用auto声明指针类型时,auto和auto*没有区别。

    int x=10;
    ​
    auto a=&x;
    ​
    auto*b=&x;    //两者没有区别,只是后者强调了b是个指针而已。

    用auto声明引用类型时,必须加上&。

    int x=0;
    ​
    auto& c=x;
  • 在同一行定义多个变量时,这些变量须是相同的类型。

    auto a=1,b=1;       // √
    ​
    auto a=3,b=3.0;    // ×

不能用的场景

  • 不能做函数的参数

    int Add(auto a,auto b){}    //×

    因为编译阶段就要将auto换成形参实际的类型,而此时实参没有传过来,编译器无法对形参的实际类型做推导。

  • auto不能直接用来声明数组

    auto arr[]={1,2,3};   //×

基于范围的for循环

对于一个有范围的集合而言,由程序员来写明循环的范围 是多余的,有时候还易犯错,因此,C++引入了基于范围的for循环。

格式:for(范围内用于迭代的变量:被迭代的范围)

#include<iostream>
using namespace std;
void TestFor()
{
    int array[] = { 1, 2, 3, 4, 5 };
    for (auto e : array)
    {
        cout << e ;
    }
}
int main()
{
    TestFor();
    return 0;
}

使用条件

for循环迭代的范围必须是确定的。

对于数组,应为第一个和最后一个元素的范围。

对于类,应提供begin和end的方法,begin和end就是范围。

指针空值---nullptr

NULL实际上是一个宏,它可能被定义为字面常量0或者void*,这可能会导致一些麻烦,因而C++引入了nullptr。

  • nullptr是一个关键字,因而在使用时不用引头文件。

  • sizeof(nullptr)与sizeof((void*)0)所占字节数相同。

  • 相比NULL,建议用nullptr,不易出错,提高代码的健壮性。

  • 13
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值