C++入门(上)

发布于:2025-02-10 ⋅ 阅读:(142) ⋅ 点赞:(0)

目录

1. C++关键字

2. 命名空间

 2.1、命名空间定义

 2.2、命名空间使用

3. C++输入 & 输出

4. 缺省参数

4.1 缺省参数概念

4.2 缺省参数分类

5. 函数重载

 5.1、函数重载概念 

5.2、C++支持函数重载的原理--名字修饰(name Mangling)


注:C++兼容C,C语言的大部分代码在C++的环境下一样可以跑起来。


1. C++关键字

C++总计63个关键字,C语言32个关键字。

2. 命名空间

补充: std是所有C++标准库的命名空间。
补充:命名空间的展开和头文件的展开本质是不一样,头文件的展开本质就是拷贝,将头文件中的内容拷贝到当前要运行的这个文件中,而命名空间的展开,是对用户开放那一块域中各种成员的访问权。在很早之前,还没有命名空间的时候,C++是有使用 #include<iostream.h> 这样的头文件的,后来为了跟C语言区别,就没有了.h后缀,反正也没影响。

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

例如:

#include <stdio.h>
#include <stdlib.h> // 该头文件是包含有rand函数的
// int rand = 10;
// 注:C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
void test1()
{
    // printf("%d\n", rand); // rand是那个生成随机数的函数名
}
// 编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”
// 命名冲突:一般情况下是程序员的命名和库函数的名称相同而产生的问题

// 补充:在C语言中可以使用::域限定符,在有局部变量的情况下去访问全局变量(这两个变量是同名的,因为不同的域可以有同名变量,但同一个域在C语言中是不可以有同名变量的)
// 注:域分为:1、局部域(默认优先访问的域) 2、全局域(::在左边什么都不加的情况下,默认访问全局域(注:函数不能代表是域)) 3、命名空间域(namespace定义出的域) 4、类域
// ex:
int x = 0;
void test2()
{
    int x = 1;
    cout << x << endl;
    cout << ::x << endl;
}

​

 2.1、命名空间定义

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。
注:命名空间域里的变量还是全局变量,但它们的名字就不会冲突了。
注:命名空间域不影响变量的生命周期。
注:1.命名空间中可以定义变量/函数/结构体。

       2.命名空间可以嵌套
       3.同一个工程中允许存在多个相同名称的命名空间,编译器最后会将同名的命名空间合并成一个命名空间!!!!!

注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中。
总结:命名空间就是对全局这些冲突东西的进行个隔离。

补充:编译器的搜索原则:1.先搜索当前局部域 2.再搜索全局域 3.如果用::指定了就会直接去指定域搜索 -- 如果这些域都搜索不到,编译器就会报错。 -- 每对{}括起来的空间都是一块域
 

 2.2、命名空间使用

有三种方式:

namespace specify
{
    int x=1;
    int y=1;

    void test()
    {
        ;
    }

    struct Node
    {
        struct Node* next;
        int val;
    };

    namespace kk
    {
        void Init()
        {
            cout << "kk" << endl;
        }
    }

    namespace ll
    {
        void Init()
        {
            cout << "ll" << endl;
        }
    }
}
void test3()
{
    // 1.加命名空间名称及作用域限定符
    printf("%d\n", specify::x);

    // 2.使用using将命名空间中某个成员引入
    using specify::x;
    printf("%d\n", x);

    // 3.使用using namespace 命名空间 名称引入
    using namespace specify;
    printf("%d\n", x); printf("%d\n", y);

    // 注:结构体的使用有点怪
    struct specify::Node* ptr;
}

注:using namespace 命名空间 -- 也叫展开命名空间。通俗一点将就是打开了该空间内各个成员的访问权限,不指定也能直接找到(就直接像全局变量一样了),当然即使再指定了也没关系。
注意:using namespace 命名空间 -- 展开命名空间只是能图方便一点,但这是很不好的。
补充:在做项目时,如果一个命名空间里的部分成员你需要大量经常的使用,那么你可以选择只展开该成员。ex:std里的 cin 和 cout,我们可以选择 using std::cin; using std::cout;

 

3. C++输入 & 输出

1.使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含<iostream>头文件以及按命名空间使用方法使用std。
2.cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<iostream>头文件中。
3.<<是流插入运算符,>>是流提取运算符。
4.使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。而且C++的输入输出可以自动识别变量类型。-- 比起C语言的printf,它们可以自动识别数据类型,不需要像C语言打印数据时还要写%d、%s...等等这些东西 

void test4()
{
    // 补充:<< 在C++中该操作符有两重意义:1.左移 2.流插入 -- << 是不是很形象,就像说明了数据的流向。
    // 这是左移
    int i = 100;
    i << 1;

    // 这是流插入
    cout << "hello world" << endl; // 补充:cout中的c是控制台console的缩写,cout << 指的就是流向控制台,在控制台打印这些数据;endl(end line缩写)是一个全局变量,它的值就是'\n',加上这个就是等价于加上换行符
    // 注:流插入 << 是可以连续的流插入的
    // ex :
    cout << "hello world" << "kk" << '\n';
    
    // 补充:>> 在C++中该操作符有两重意义:1.左移 2.流提取
    // 这是右移
    int j = 100;
    j >> 1;
    
    // 这是流提取
    cin >> j;
    // 注:流提取 >> 是可以连续的流提取的
    // ex : 
    char ch = 0;
    cin >> j >> ch;
}

4. 缺省参数

4.1 缺省参数概念

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

void Func1(int a = 0) // 缺省参数
{
    cout<<a<<endl;
}
void test5()
{
    Func1(); // 没有传参时,使用参数的默认值
    Func1(10); // 传参时,使用指定的实参
}

4.2 缺省参数分类

1、全缺省参数

​
// 该函数只有四种传参方式,Func2(1,2,3)(全部都传)、Func2(1,2)(传给前两个)、Func2(1)(传给第一个)、Func2()(一个都不传)
void Func2(int a = 10, int b = 20, int c = 30)
{
    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;
    cout<<"c = "<<c<<endl;
}
// 注:参数只能依次传过去,不能说跳过a把参数传给b和c这样的。

​

2、半缺省参数 -- 从右往左连续给
 

​
// 该函数只有三种传参方式,Func3(1,2,3)(全部都传)、Func3(1,2)(传给前两个)、Func3(1)(传给第一个)
void Func3(int a, int b = 10, int c = 20)
{
    cout<<"a = "<<a<<endl;
    cout<<"b = "<<b<<endl;
    cout<<"c = "<<c<<endl;
}
// 注:调用半缺省类型的函数时,必须传参,不接受无参数的情况,形参有几个没初始化,就至少要传几个参数。

注意:

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给(要给就从右往左依次给,要不然就全都不要给(全部都没初始化,那就是原本C语言中的函数了))!!
  2. 缺省参数不能在函数声明和定义中同时出现(要出现也只能在声明中出现,不能在定义中出现)
  3. 缺省值必须是常量或者全局变量 -- 注:一般用常量,不怎么用全局变量
  4. C语言不支持(编译器不支持)

总结:有了缺省参数之后,在写一些数据结构(例如 栈 )的初始化可以更加的灵活。

例如:

struct Stack
{
    int* a;
    int capacity;
    int size;
};

void StackInit(struct Stack* pst, int n = 4) // 当你确定最多要存多少个数据时,就可以给 n 传值,这样就能避免了扩容的消耗
{
    pst->a = (int*)malloc(sizeof(int) * n);
}

5. 函数重载

注意:返回值不同不能构成函数重载。

自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了!”

 5.1、函数重载概念 

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

1、参数类型不同

int Add(int left, int right)
{
    cout << "int Add(int left, int right)" << endl;
    return left + right;
}
double Add(double left, double right)
{
    cout << "double Add(double left, double right)" << endl;
    return left + right;
}

2、参数个数不同

void f()
{
    cout << "f()" << endl;
}
void f(int a)
{
    cout << "f(int a)" << endl;
}

3、参数类型顺序不同 -- 本质也是类型不同

void f(int a, char b)
{
    cout << "f(int a,char b)" << endl;
}
void f(char b, int a)
{
    cout << "f(char b, int a)" << endl;
}

 

5.2、C++支持函数重载的原理--名字修饰(name Mangling)

为什么C++支持函数重载,而C语言不支持函数重载呢?
答:在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接

  1. 实际项目通常是由多个头文件和多个源文件构成,而通过C语言阶段学习的编译链接,可以知道,【当前a.cpp中调用了b.cpp中定义的Add函数时】,编译后链接前,a.o的目标文件中没有Add的函数地址,因为Add是在b.cpp中定义的,所以Add的地址在b.o中。
  2. 于是链接阶段就是专门处理这种问题,链接器看到a.o调用Add,但是没有Add的地址,就会到b.o的符号表中找Add的地址,然后链接到一起。
  3. 那么链接时,面对Add函数,链接接器会使用哪个名字去找呢?这里每个编译器都有自己的函数名修饰规则。
  4.  由于Windows下vs的修饰规则过于复杂,而Linux下g++的修饰规则简单易懂,所以用g++演示了这个修饰后的名字。        
  5.  通过截图我们可以看出gcc的函数修饰后名字不变。而g++的函数修饰后变成【_Z+函数长度+函数名+类型首字母】。结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。
  6. 通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
  7. 如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。

总结:C语言在链接时,是直接用函数名去找地址的,有同名函数,区分不开。-- C语言不支持函数重载的原因。C++有了函数名修饰规则,就是在函数名中引入参数类型,参数类型不同不就是函数名不同了嘛(但是祖师爷没有规定明确的规则具体如何,所以每个编译器都有自己的一套函数名修饰规则)。-- C++支持函数重载的原因。
 


网站公告

今日签到

点亮在社区的每一天
去签到