在C语言基础上的C++第一章(基础的输入输出和动态内存开辟)

1:C++的历史

C++ 的历史可以追溯到 20 世纪 70 年代末,其发展历程如下:

1:起源与诞生

  • 1979 年,丹麦计算机科学家比雅尼・斯特劳斯特鲁普(Bjarne Stroustrup)在贝尔电话实验室(现诺基亚贝尔实验室)为其博士论文进行开发工作。他接触到了主要用于模拟的 Simula 67 语言,该语言是第一个支持面向对象编程范式的语言,但速度太慢不实用。于是,斯特劳斯特鲁普开始在 C 语言基础上进行改进,开发出了最初被称为 "C with Classes" 的语言,它包含了类、基本继承、内联、默认函数参数和强类型检查等特性。

2:发展与完善

  • 命名确定与商业化:1983 年,"C with Classes" 更名为 "C++"。1985 年,斯特劳斯特鲁普的《The C++ Programming Language》一书出版,同年 C++ 作为商业产品被实现。
  • 标准制定与更新:1989 年,C++ 进行更新,加入了保护和静态成员以及多类继承等特性。1998 年,C++ 标准委员会发布了第一个国际标准 ISO/IEC 14882:1998,即 C++98,其中包含了标准模板库(STL)。2003 年,委员会对 C++98 中的多个问题进行了修订,形成了 C++03 标准。
  • 技术拓展与突破:2005 年,C++ 委员会发布技术报告 TR1,计划为新标准添加多种特性。2011 年中期,C++11 标准完成,该标准受 Boost 库项目影响较大,加入了正则表达式支持、综合随机化库、新的 C++ 时间库、原子操作支持、标准线程库、新的 for 循环语法、auto 关键字、新的容器类、更好的联合和数组初始化列表支持以及可变参数模板等新特性。

2: 在C语言的基础上理解C++的代码

以打印出hello world为例

我们可知C语言的代码是

cpp 复制代码
#include<stdio.h>

int main()
{
printf("hello world");
return 0;
}

C++的代码是

cpp 复制代码
#include<iostream>
using namespace std;

int main()
{
cout<<"hello world"<<endl;
return 0;
}

两个代码相似的代码还是很多的,我们慢点来解释c++代码中的区别。

1:C++的头文件

通过C语言的学习我们知道,要使用某个库函数,需要包含对应的头文件,而在C++中,系统头文件的包含一般不带.h后标。

而在下面加上using namespace std;又有其他含义

  • 在 C++ 中,namespace(命名空间)是一种将相关的全局实体(如类、函数、变量等)组合在一起的机制。std是标准库(Standard Library)的命名空间,它包含了 C++ 标准库中的所有类型、函数和对象,比如iostream中的coutcinvectorstring等数据结构和相关操作函数等。
  • 当使用using namespace std;时,它的作用是将std命名空间中的所有名字(标识符)引入到当前的作用域中。这样,在后续的代码中,就可以直接使用std命名空间中的类型和函数,而不需要在每次使用时都加上std::前缀。

2:库函数和标准输入流的区别(cin标准输入流为例)

从定义和本质来看

  • cin的定义 :在 C++ 中,cinistream类的一个实例对象,它被定义在<iostream>头文件中,主要用于从标准输入设备(通常是键盘)读取数据。

  • 与库函数的本质区别 :库函数是一段完成特定功能的代码块,通过函数调用的方式来执行特定的任务,而cin作为一个对象,它通过重载>>运算符来实现数据的输入功能,这与库函数的调用方式和实现机制有着本质的区别。

从使用方式和特性来看

  • 使用方式cin通过>>运算符与变量结合使用来实现数据输入,如cin >> num;,这种方式与库函数通过函数名加参数列表的调用方式完全不同。

  • 特性cin具有一些特殊的性质和行为,例如它会自动跳过空白字符,直到遇到非空白字符才开始读取数据,并且它可以连续使用>>运算符来输入多个不同类型的数据,这些特性都是基于其作为输入流对象的本质而实现的,与库函数的特性也有很大差异。

3:endl是什么

在 C++ 中,endl是一个特殊的流操纵符 ,主要用于cout等输出流对象。

功能

  • 换行与刷新缓冲区endl的主要作用是在输出流中插入一个换行符,使后续的输出内容从新的一行开始。同时,它还会刷新输出缓冲区,确保输出的数据立即显示在屏幕或其他输出设备上,而不是等待缓冲区满了才输出。

语法格式

  • 当使用cout输出时,endl直接作为<<运算符的操作数,紧跟在要输出的内容之后。例如:cout << "Hello, World!" << endl;,这段代码会先输出字符串 "Hello, World!",然后执行endl的功能,即换行并刷新缓冲区。

与其他换行方式的比较

  • \n的区别 :在 C++ 中,\n也可以实现换行功能,但它只是单纯地插入一个换行符,不会刷新缓冲区。而endl不仅换行,还能保证输出的及时性,这在一些需要实时显示输出结果的场景中非常重要,比如在调试程序或实时显示程序运行状态时,使用endl可以让输出结果立即呈现,而使用\n可能会因为缓冲区未刷新而导致输出延迟。

  • flush的区别flush也是一个流操纵符,它的主要作用就是刷新输出缓冲区,而不进行换行操作。endl相当于\nflush的结合体,既实现了换行又刷新了缓冲区。

3:C++对C的扩充

1:函数重载

我们在C语言里面学过,函数名只能表示一个功能的函数,但是在C++里面,函数可以通过改变接受的参数类型来定义多个函数。例如

在C语言里面我们只能定义一个max函数(这是函数的声明)

cpp 复制代码
int max(int a,int b);

但是在C++里面我门可以改变函数参数类型来定义多个max函数

cpp 复制代码
int max(int x,int y);
int max(int x,float y);
int max(float x,float y);

注意:只能是不同类型的参数才能重载函数,使用typedefe双重定义新的基础类型变量,是不被允许重载函数的。

2:函数自带默认参数

例如下面的函数声明

cpp 复制代码
int max(int x=10086,int y=10085);

我们可以在函数的声明上让参数带上默认值,在函数定义中带上默认值是不被允许的。

如果我们在写程序时,不传递参数,那么max函数里面的x和y会被默认成10086和10085.

注意:

如果一个函数有多个默认参数,则形参分布中,默认参数应该从右往左逐渐定义。当调用函数时,传值操作,只能从左往右定义。

例如

cpp 复制代码
int max(int a,int b=3,int c=2,int d=1);//ok
int max(int a,int b,int c=888,int d);//error
int max(int a,int b=88,int c,in d=99);//error

4:C++的输入和输出

C++为了方便用户,允许我们使用printf和scanf进行数据的输入和输出。

当然也增加了标准输入和输出流对象,cout和cin。

1:cout表示输出

在C语言里面<<表示左移运算符,而在C++里面它呗赋予了"插入"含义,作为输出信息时的插入运算符。

cout语句的格式一般为

cout<<表达式1<<表达式2<<.......<<表达式n;

2:cin表示输入

>>在C++里面被称为提取运算符。使用cin来控制标准输入流,使用>>来让输入的数据存入到内存中。

cin语句的格式一般为

cin>>变量1>>变量2>>........>>变量n;

cin对比scanf的好处是,不需要指定数据类型,而且可以忽略空格键,指标键,回车键等。

5:引用的概念

在 C++ 中,引用是一种给变量起别名的机制,它为已存在的变量提供了一个替代名称,使我们可以通过这个别名来访问和操作原变量。

例如

cpp 复制代码
int a=10086;
int &pa=a;

上述代码,可以这样理解,我们把a的名字给了pa,它们两个只是名字不同,其他的数据类型,在内存中所占空间,都是一样的。如果pa变了则a也会变。

拿个交换函数举例子

cpp 复制代码
void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}
int main() {
    int x = 5, y = 10;
    swap(x, y);
    // 此时x的值为10,y的值为5
    return 0;
}

6:new和delete运算符

在C语言中我们使用malloc和free来对内存进行动态管理,而在C++中我们使用上述两个运算符来动态分布和撤销。

区别:

1:功能特性

  • 类型安全性
    • new/delete :具有类型安全性,在使用new操作符分配内存时,会根据对象的类型自动计算所需内存空间的大小,无需显式指定。并且返回的指针类型与所分配对象的类型严格匹配,在编译时就能进行类型检查,减少了因类型不匹配导致的错误。
    • malloc/free :是 C 语言遗留下来的内存管理函数,相对缺乏类型安全性。malloc函数只负责分配指定字节数的内存空间,返回的是void*类型的指针,需要进行强制类型转换才能赋值给具体类型的指针变量,这种强制类型转换在编译时不会进行类型检查,可能会引发类型不匹配的错误。
  • 构造函数与析构函数调用
    • new/delete :当使用new创建对象时,会自动调用对象的构造函数进行初始化操作,确保对象处于一个有效、可用的状态;而在使用delete释放对象时,会自动调用对象的析构函数,进行资源的清理和释放,如关闭文件、释放动态分配的其他资源等。
    • malloc/free :这两个函数只是单纯地进行内存的分配和释放操作,不会自动调用对象的构造函数和析构函数。如果使用malloc分配的内存用于存储对象,那么在使用前需要手动调用构造函数进行初始化,在释放内存前也需要手动调用析构函数进行资源清理,否则可能会导致资源泄漏或对象状态异常等问题。

2:语法形式

  • 操作符与函数
    • new/deletenewdelete是 C++ 中的操作符,它们的语法更加简洁明了,与 C++ 的语言特性结合得更加紧密,使用起来更加自然、方便,符合 C++ 的面向对象编程风格。
    • malloc/freemallocfree是 C 语言中的函数,在 C++ 中也可以使用,但它们的语法相对较为繁琐,需要包含<stdlib.h>头文件,并且在调用时需要传递内存大小等参数,与 C++ 的一些高级特性集成不够好。
  • 数组分配与释放
    • new/delete :使用new[]delete[]可以方便地进行数组的分配和释放操作。new[]会自动计算数组所需的内存空间大小并进行分配,返回指向数组首元素的指针;delete[]则会正确地释放整个数组所占的内存空间。
    • malloc/free :使用malloc分配数组时,需要手动计算数组所需的内存空间大小,然后进行分配,返回的指针同样需要进行强制类型转换才能使用。在释放数组内存时,free函数无法自动识别数组的大小和边界,需要程序员确保传递给free的指针是正确的,并且要保证释放的是整个数组所占的内存空间,否则容易导致内存泄漏或其他错误。

3:内存分配失败处理

  • 异常与返回值
    • new/delete :当new操作无法分配到足够的内存时,它会抛出bad_alloc异常,这种异常处理机制与 C++ 的异常处理体系相结合,使得程序能够更方便地进行错误处理和资源回收。
    • malloc/freemalloc函数在分配内存失败时,会返回NULL指针,程序员需要在每次调用malloc后检查返回值是否为NULL,以判断内存分配是否成功,这种错误处理方式相对较为繁琐,容易被忽略,从而导致程序出现不可预测的错误。

4:适用场景

  • 面向对象编程与 C++ 特性
    • new/delete:更适合在 C++ 的面向对象编程中使用,特别是在处理对象的动态创建和销毁时,能够与构造函数、析构函数等 C++ 特性无缝结合,保证对象的正确初始化和资源的有效清理,提高了代码的可读性和可维护性。
    • malloc/free :在一些对性能要求极高且不需要调用构造函数和析构函数的简单数据类型的内存分配场景中,或者在与 C 语言代码进行交互的混合编程环境中,mallocfree可能仍然有一定的使用价值,但在纯 C++ 代码中,其使用场景相对较少。
相关推荐
星蓝_starblue30 分钟前
单元测试(C++)——gmock通用测试模版(个人总结)
c++·单元测试·log4j
qq_433554541 小时前
C++ 面向对象编程:友元、
开发语言·c++
lover_putter1 小时前
ai学习报告:训练
人工智能·学习
123yhy传奇2 小时前
【学习总结|DAY020】Java FIle、字符集、IO流
java·开发语言·学习
XZHOUMIN3 小时前
【MFC】如何修改多文档视图的标签
c++·mfc
eddieHoo3 小时前
关于生活的事
学习
越甲八千3 小时前
重拾设计模式--外观模式
c++·设计模式·外观模式
刘好念4 小时前
[OpenGL]使用TransformFeedback实现粒子效果
c++·计算机图形学·opengl
蓝胖子教编程4 小时前
【题解】【枚举】——[NOIP2018 普及组] 龙虎斗
c++·算法
是呆瓜呀4 小时前
12.13-12.21 刷题汇总
学习