【C++】认识C++(上)

目录

从C到C++

C语言的出现是计算机科学和工程史上的一个重要里程碑,许多现代计算机语言都受C语言的影响。C语言是面向过程的,结构化和模块化的语言。 C语言的设计者必须细致地设计程序中的每一个细节,准确考虑程序运行时每一个步骤发生的事,但是随着计算机应用的推广,需要解决更多更复杂的问题,C语言就太适合了。

于是乎,20世纪80年代,计算机界提出了面向对象的程序设计OOP(Object Orientde Programming)思想,由此支持面向对象的程序设计语言应运而生。

1982年,AT&T Bell(贝尔)实验室的Bjarne Stroustrup博士及其同事在C语言的基础上引入并扩充了面向对象的概念,为了表达该语言与C语言的渊源关系,将其命名为C++。

C++保留了C语言原有的主要优点,增加了面向对象的机制。 又因为C++是C发展而来的,与C兼容,用C语言编写的程序基本上可以不加修改的用于C++。C++是C的超集,可用于面向过程的结构化设计,但更多地用于面向对象的程序设计,是一种功能强大的混合型的程序设计语言

下面就来走进C++,敲开C++的大门。

命名空间

同名冲突

在C/C++中,变量,函数和C++中要学习到的类都是大量存在的。由于它们的大量存在,我们就喜欢将它们定义在全局作用域上,以方便修改。

在实际生活中,通常会将一个项目分到几个小组去写,最后整合,这样时间效率都会增高。但是这就会出现一个问题,在整合项目的时候,可能会有些命名上的重复,导致程序报错。

不仅如此,在程序设计中往往需要引用一些库(包括C++编译系统提供的库,以及开发者字节开发的库等等),为此需要包含有关头文件。如果在这些库中包含有与程序的全局实体同名的实体,或者不同库有相同的变量或函数或类的名字,就会在编译时出现名字冲突,也就是全局命名空间污染。 那么如何解决这个问题,C++中namespace 关键字的存在就是针对这种问题的。

我们已经说了,写程序时容易发生命名冲突,则使用命名空间的目的就是对标识符的名称本地化,以避免命名冲突或文字污染。命名空间实际上就是一个由程序设计者命名的内存区域。所以一开始在学C语言的时候,我们就知道了有两个作用域,一个是全局域,另一个是局部域,现在加了这个命名空间以后,C++的一个程序上就会有三个域的存在

  1. 全局域
  2. 局部域
  3. 命名空间域

例如:

c 复制代码
#include<stdio.h>
#include<stdlib.h>

int rand = 10;
int main()
{
	printf("%d\n", rand);
	return 0;
}

rand本身是一个库函数,用来生成随机值,要用的时候需要包含在头文件stdlib.h中。

而此时全局变量中又定义了一个rand,导致printf在输出时不知道应该调用库函数里面的rand,还是全局变量中的rand,此时就发生了命名冲突。

命名空间的定义

定义命名空间,需要使用到namespace关键字,后面接着命名空间的名字,最后加一对{}即可,{}中即为命名空间的成员。命名空间中不止可以包括变量,还可以包括函数,结构体,类(后面会学习到),模板,命名空间(嵌套使用也可以)等,例如下面的例子:

cpp 复制代码
namespace ns1
{
    int a = 1;//定义变量

    int Add(int x, int y)//定义函数
    {
        return x+y;
    }

    struct ListNode//定义结构体
    {
        int val;
        struct ListNode* next;
    };

    namespace ns2//嵌套了另一个命名空间
    {
       int sub(int x, int y)
       {
           return x+y;
       }
    }
}

还有值得要提的一点是,同一个工程中存在多个相同名称的命名空间,编译器最后都将会合成在同一个命名空间中。 就像你在北京有套房,在上海也有套房,那别人也就知道你是有两套房,都在你的名下。

如:

cpp 复制代码
namespace ns1
{
	int a = 1;//定义变量
	int b = 2;
}

namespace ns1
{
	int Add(int x, int y)//定义函数
	{
		return x + y;
	}
}

最终编译器会认为是:

cpp 复制代码
namespace ns1
{
	int a = 1;//定义变量
	int b = 2;
	int Add(int x, int y)//定义函数
	{
		return x + y;
	}
}

命名空间所定义的命名空间域,将命名空间中的内容都局限于该命名空间中。

命名空间的使用

我们介绍了如何去定义命名空间,下面来看看它是如何使用的。

命名空间有三种使用方式:

  • 第一种方式:加命名空间名称及作用域限定符(在需要使用命名空间中的内容时再制定访问)
cpp 复制代码
#include<stdio.h>
namespace ns1
{
	int a = 1;//定义变量
	int b = 2;
}
int main()
{
//    printf("%d\n", a);
//     这种直接想打印命名空间域里面的变量时编译器会报错
//     error C2065: "a": 未声明的标识符
    printf("%d\n",ns1::a);
    return 0;
}

我们发现在命名空间名字的后面有一个::符号,这叫做域作用限定符, 也就限定了内容在哪块查找。如:

cpp 复制代码
#include<stdio.h>
int a = 10;
int main()
{
	int a = 20;
	printf("%d\n", a);   //打印的是局部域中的a是20
	printf("%d\n", ::a); //打印的是全局域中的a是10
	return 0;
}
  • 第二种方式:使用using将命名空间中某个成员引入

即在程序中需要大量使用命名空间中的某一个成员时,每一次都去加上限定符比较麻烦,于是我们用using来制定展开某一个,例如:

cpp 复制代码
#include<stdio.h>
namespace ns1
{
	int a = 1;//定义变量
	int b = 2;
}
using ns1::a;
int main()
{
	printf("%d\n", a);
	printf("%d\n", a);//此时我们使用a时就不用再加上限定符
	printf("%d\n", ns1::b);//我们只是展开了变量a,所以在使用b的时候还是要加上限定符
	return 0;
}
  • 第三种方式:使用using namespace命名空间名称引入

使用了这个命名空间引入,可以将命名空间中的变量所有都展开来,后面在使用时都不需要加上限定符。例如:

cpp 复制代码
#include<stdio.h>
namespace ns1
{
	int a = 1;//定义变量
	int b = 2;
}
using namespace ns1;
int main()
{
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

注意!:编译器在编译时对变量等的查找会有默认查找顺序,首先查找的是当前局部域,其次是全局域。

只有命名空间被展开了,最后才会到这里面去找。

C++的输入和输出

C语言的输入输出是有scanf和printf来控制的,而在C++中则是由cin和cout来控制。

例如:

cpp 复制代码
#include<iostream>
using namespace std;
int main()
{
	int a, b;
	cin >> a >> b;
	cout << "a = " << a << "  " << "b = " << b << endl;
	return 0;
}

结果如下:

我们发现该程序进行了对a和b的输入并且打印输出操作。

在C++中,cout和cin是系统定义的对象名,cin是标准输入对象(键盘),cout是标准输出对象(控制台),(我们运行结果出来的框框就是控制台)。

<<是流插入运算符 ,与cin配套使用,>>是流提取运算符 ,与cout配套使用。

在C语言中,scanf和printf的使用需要包含头文件<stdio.h>,C++中也一样,在使用cin和cout时,需要包含头文件 < iostream>。

C++标准库中的类和函数时再命名空间std中声明的,如果需要使用C++标准库中的内容,就需要使用using namespace std来声明,表示要用命名空间std中的库函数

使用C++的输入输出更加方便,不需要像printf/scanf那样,需要手动控制格式C++的输入输出可以自动识别变量类型。

注意:

std是C++标准库的命名空间,应该如何去使用

  1. 在日常练习中,可以直接使用using namespace std 即可,方便使用各种库函数。
  2. 因为using namespace std展开,标准库就全部露出来了,如果我们定义根库重名的类型/对象/函数,就存在冲突问题,这种问题在日常练习中很少出现,但是项目开发中代码较多,规模较大,就很容易出现问题,所以在项目开发中建议使用:像std::cout这样使用时指定命名空间 + using std::cout展开常用的库对象/类型等方式。

缺省参数(默认参数)

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

缺省参数的分类

  • 全缺省参数(全默认参数)
    也就是函数的形参全部给了默认值
cpp 复制代码
#include<iostream>
using namespace std;
void func(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a;
	cout << " b = " << b;
	cout << " c = " << c << endl;
}

int main()
{
	func(1, 2, 3);
	func(1, 2);
	func(1);
	func();
	return 0;
}

运行结果如下:

  • 半缺省参数
cpp 复制代码
#include<iostream>
using namespace std;
void func(int a , int b = 20, int c = 30)
{
	cout << "a = " << a;
	cout << " b = " << b;
	cout << " c = " << c << endl;
}

int main()
{
	func(1, 2, 3);
	func(1, 2);
	func(1);
	return 0;
}

运行结果如下:

在使用缺省参数时有几个注意事项如下:

  1. 半缺省参数必须从右往左依次来给出,不能间隔着给
  2. 缺省参数不能在函数的声明和定义中同时出现
  3. 缺省值必须是常量或者是全局变量
  4. C语言不支持。

对于第2点的解释:

我们对一个函数进行声明和定义时,通常把声明放在.h文件中,定义放在.cpp文件中。

所以会出现:

xxx.h文件中函数声明:void func(int a = 10);

xxx.cpp文件中函数定义:void func(int a = 20){...}

此时两个位置提供的值不同,编译器就不知道该用那个缺省(默认)值,会报错。

今天的内容到此结束啦,感谢大家观看,如果大家喜欢,希望大家一键三连支持一下,如有表述不正确,也欢迎大家批评指正。

相关推荐
2301_8010741512 分钟前
ArkTs组件(2)
开发语言·前端·华为·harmonyos
yep吖13 分钟前
Datawhale-AI冬令营二期
开发语言·javascript·ecmascript
L.S.V.37 分钟前
Java 溯本求源之基础(三十一)——泛型
java·开发语言
Redamancy_Xun43 分钟前
开源软件兼容性可信量化分析
java·开发语言·程序人生·网络安全·测试用例·可信计算技术
ZLRRLZ1 小时前
【C++】多态
开发语言·c++
m0_748246611 小时前
【论文投稿】Python 网络爬虫:探秘网页数据抓取的奇妙世界
开发语言·爬虫·python
minstbe1 小时前
AI开发 - 算法基础 递归 的概念和入门(二)汉诺塔问题 递归的应用和使用注意 - Python
开发语言·python·算法
岁月如歌,青春不败2 小时前
HMSC联合物种分布模型
开发语言·人工智能·python·深度学习·r语言
TANGLONG2222 小时前
【初阶数据结构与算法】八大排序之非递归系列( 快排(使用栈或队列实现)、归并排序)
java·c语言·数据结构·c++·算法·蓝桥杯·排序算法
不想当程序猿_2 小时前
【蓝桥杯每日一题】与或异或——DFS
c++·算法·蓝桥杯·深度优先