当前位置: 首页 > news >正文

从C语言到C++:拥抱面向对象编程的全新世界

目录

引言

一、C++发展历史

二、C++参考文档

三、C++的第一个程序

3.1、头文件的包含

3.2、打印 hello world!

3.3、命名空间的使用

四、命名空间

4.1、namespace 的价值

4.2、namespace 的定义

4.3、命名空间的展开

五、C++的输入/输出

5.1、

5.2、std::cin 

5.3、std::cout

5.4、std::endl

5.5、printf 和 scanf

六、缺省参数(默认参数)

6.1、缺省参数的定义

6.2、全缺省和半缺省

七、函数重载

八、引用

8.1、引用的概念和定义

8.2、引用的特性

8.3、引用的使用

8.4、const引用

8.5、引用和指针的关系

九、inline

9.1、inline 的使用

9.2、inline 的意义

9.3、inline 的注意事项

十、nullptr

结语


引言

恭喜你!历经千辛万苦,你已经成功地掌握了C语言的基础知识。现在,你拥有了编写底层程序、理解计算机运作方式的能力。但你是否已经感受到,在解决一些更复杂的问题时,C语言可能显得力不从心?想要构建更大型、更易维护,更贴近现实世界的软件系统吗?那么,是时候踏入C++的世界了!

C++作为C语言的超集,不仅保留了C语言的强大功能,更引入了面向对象编程 (OOP) 这一核心思想。这意味着你可以将复杂问题分解为一个个相互协作的“对象”,从而让代码更易于理解、组织和扩展。 在C语言的基础上学习C++,就像从一辆自行车升级到一辆汽车。 虽然都需要你掌握一定的「驾驶」技巧,但C++赋予了你更加强大的“引擎”和“驾驶舱”,让你体验更广阔、更多姿多彩的“编程之旅”。

本系列的博客将带你快速入门C++,为你构建OOP编程的坚实基础,助你成为一名更优秀的程序员。让我们一起开始这趟充满乐趣的旅程吧!

一、C++发展历史

C++由丹麦计算机科学家Bjarne Stroustrup于1979年在贝尔实验室创建,最初被称为“C with Classes”,旨在为C语言增加面向对象编程(OOP)的特性。

  • 1979年: Stroustrup开始设计C++,最初的目标是增强C语言,使其更适合大型项目。

  • 1983年: 正式命名为C++。

  • 1980年代: C++不断发展,引入了虚函数、运算符重载等关键特性,逐渐成为面向对象编程的主流语言之一。

  • 1998年: ISO/IEC发布了C++标准 (C++98),奠定了C++语言的基石。

  • 2011年: 发布了C++11标准,引入了许多现代编程特性,如智能指针、lambda表达式等,极大地提高了开发效率和代码质量。(之后的标准不断更新,例如C++14, C++17, C++20,旨在不断优化语言,提升开发体验)

C++ 至今仍在不断发展,拥有强大的生命力,广泛应用于系统编程、游戏开发、高性能计算等领域。目前最新的标准是C++23,听说明年还会有C++26。

二、C++参考文档


https://legacy.cplusplus.com/reference/

https://en.cppreference.com/w/

三、C++的第一个程序

#include <iostream>using namespace std;
int main()
{cout << "hello world!" <<endl;return 0;
}

3.1、头文件的包含

我相信大家在C语言都学过了,这里就不多说了。需要注意的是,C++这里的头文件 .h后缀是没有的,只有老式的C头文件中才有 .h 的后缀。

有些C头文件的后缀也可以省略,但是要在最前面加上c,转化为C++头文件

eg. <math.h>  转化成  <cmath>

3.2、打印 hello world!

C++的输出和输入是不同的,C++用 cin 和 cout 来输入和输出,其语法形式由上述代码所。这里只是简单介绍,不做过多阐述

3.3、命名空间的使用

cout 和 endl 都属于 std 这个命名空间的,如果不标明使用该命名空间,编译器将无法理解 cout,这就是using namespace std; 的由来。同样,这里只是简单介绍,后续会详细讲解

四、命名空间

4.1、namespace 的价值

C++ 是在祖师爷为了解决C语言一些问题而改进出来的,而这个namespace就是为了解决C语言命名冲突的问题的。在一个项目中,会有多个程序员,多个项目组组成,每个程序员都会按照自己的喜好为变量命名,但是,你有没有想过,变量的命名不会冲突吗?确实会冲突。

当变量,函数和类的名称大量存在于全局作用域中,可能会导致冲突,而namespace则完美的结局了这个问题。比如:高三年级有两个张三,一个是(1)班,一个(3)班,只要在叫张三前指定班级,就不会搞混了。

4.2、namespace 的定义

(1)namespace 为关键字,后面跟命名空间的名字,具体语法形式如下:

namespace tong
{//成员,可以为变量,也可以为函数,结构体等int n = 10;int Add(int* x ,int* y){int tmd = *x;*x = *y;*y = tmd;}//…………
}

(2)namespace 的本质是定义出一个域,这个域与全局域各自独立,不同的域可以定义同一个变量名,使用时,只要标明变量来自哪个域就好了

(3)namespace 只能定义在全局,但是可以嵌套定义

(4)同一个项目中,同名namespace会被认为是同一个命名空间,不会发生冲突

(5)C++的标准库都放在一个叫 std 的命名空间中

4.3、命名空间的展开

命名空间的展开有多钟形式:

(1)展开命名空间中的所有成员

using namespace std;

像C++第一个程序一样,这样的方式就展开了该命名空间中的所有成员,但是,展开这么多成员会增大与其他变量,函数和类冲突的风险。因此,这种方式只适合个人练习使用,不推荐做项目的时候用

(2)展开命名空间的部分成员

using std::cout;

这种方式,只展开一个我们需要的成员,大大降低了冲突的风险,推荐对项目中经常使用且不会发生冲突的成员使用

(3)指定命名空间访问

std::cout << "hello world!" << std::endl;

我愿将其称为随用随标的形式,这种方式是绝对不会发生冲突的,项目中推荐这种方式

五、C++的输入/输出

5.1、<iostream>

iostream 是 input output stream 的缩写意思是标准输入/输出流

5.2、std::cin 

std::cin 是istream类的对象,它主要面向窄字符的标准输入流,用法如下:

int a = 0;
std::cin >> a;

与C语言不同的是,cin 是自动识别变量类型的,就不需要什么 %d 了,也不需要什么取地址符了,从这一点来说,C++能方便点

>>是流插入运算符,与 cin 搭配使用;相应的,>>是流提取运算符,与cout搭配使用

5.3、std::cout

std::out 是 ostream类的对象,它主要面向窄字符的标准输出流,用法如下:

int a = 0;
std::cin >> a;
std::cout << a << std::endl;
std::cout << "hello world!" <<std:endl;

它也是一样,自动识别数据的类型

5.4、std::endl

std::endl 是一个函数,流插入输出时 ,相当与一个换行符,同时也有刷新缓冲区的功能

5.5、printf 和 scanf

C++是包含C语言的,因此,也可以用C语言的方式去实现输入和输出。

六、缺省参数(默认参数)

6.1、缺省参数的定义

        在声明或定义函数时,函数的参数有一个指定的值。在调用函数未传参时,将采用这个默认值为参数

void test1(int n = 10)
{std::cout << n << std::endl;
}

例如上述代码,n就是个缺省参数,当调用test1时,如果传 20 就会打印20,如果什么都不传,就会打印10.

6.2、全缺省和半缺省

全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左 依次连续缺省,不能间隔跳跃给缺省值。

带缺省参数的函数调⽤,C++规定必须从左到右依次给实参,不能跳跃给实参。

void test(int a, int b = 10, int c = 30);test(20 , , 40);  //错误
test(20);  //正确

七、函数重载

C++支持在同一作用域内出现同名函数,但是要求这些同名函数的形参不同,比如:形参的数量,形参的类型。C语言是不支持重载函数的!

举几个例子:

void test1(int a,int b);
void test1(int a);    //构成重载函数int test1(int a,int b); //不构成重载该函数

但是,如果重载函数调用产生歧义,编译器是会报错的:

void test(int a ,int b = 10);
void test(int a);//调用产生歧义
test(20);

八、引用

8.1、引用的概念和定义

引用就是给变量取了一个别名,编译器不为引用开辟空间,引用的语法形式如下:

类型& 引用别名 = 引用对象

举个例子就是:

int a = 10;
int& b= a;

由于张三只有三根头发,因此张三有个别名叫三毛。张三今天赚了100元和三毛赚了100元是一样的。也就是说,对引用的操作,会直接影响到被引用的对象。 

8.2、引用的特性

(1)引用在定义的时候必须初始化,引用没有空引用这一说法,定义时必须初始化,总不能创建一个别名,却不知道给谁取别名吧

(2)引用一旦定义后,就不能更改。比如,定义y为x的别名,到后面,y必须一直是x的别名,不能更改为z的别名

(3)一个变量可以有多个引用

8.3、引用的使用

(1)引用做形参

这是最简单的交换函数:

void Swap(int* x ,int* y)
{int tmd = *x;*x = *y;*y = tmd;
}

在C语言中,我们必须传递指针才能完成交换这一行为,但是有了引用就不一样了,前面说了,对引用操作会直接影响到被引用对象,那么,像这种情况,可不可以传递一个别名过去呢?当然可以

void Swap(int& x, int& y)
{int tmd = x;x = y; y = tmd;
}

这样写,调用函数不在拷贝原参,提高了代码效率。那么,引用可以代替指针吗?这个问题的答案留在后面,看看你想的对不对。

(2)引用做返回值

函数返回的是引用,这是怎么回事呢?首先要搞明白函数是如何返回参数的。例如加法函数:

int Add(int x,int y)
{return x+y;
}

编译器会把x+y储存到一个临时变量中,然后再将这个临时变量返回,函数运行完后,该临时变量也就销毁了。而返回引用,就是返回别名,谁的别名?当然是这个临时变量的别名。所以,我可以通过这个别名来篡改这个已经释放掉的临时空间?——是的。因此,在某些情况下,返回引用时非常危险的行为。当然,如果非要返回引用,可以给变量加一个static,或者保证该变量在函数走完后根本不会被释放。

8.4、const引用

(1)当我们创建一个变量 int a = 10;我们可以对 a 进行读取,也可以对 a 进行修改,但是,当变量 a 被 const 修饰,那么 a 就不能被修改,只能被读取。而这种读取,我们称为权限

int a = 10;        //可以读,可以写
const int a = 10;  //只能读,不能写

那如果我引用一个只能读的常量,然后对其修改:

const int a = 10;
int& b = a;
b = 20;       //可行吗?

在C++语法上时不行的,因此此举对 a 的权限进行了放大,本来 a 只能读,引用后成了又能读又能写,这是不对的。权限放大不行,那么权限缩小可以吗?当然可以,使用引用的时候可以权限缩小,但是不能权限放大!

注意:const 引用可以引用常量:

const int& a = 10;  //const引用可以引用常量

(2)设涉及表达式和类型转换的引用

int a = 10;
int& ra = a*3;   //涉及到了表达式的引用double b = 3.14;
int& rb = b;     //涉及到了类型转换的引用

变量是 a ,a*3 仅仅是一个表达式,那么 ra 是哪个空间的引用呢? a*3 会被放到一个临时对象中,而这个ra就是这个临时空间的变量,但是,C++的临时对象具有常性,只能读不能写,因此要在前面加上const 。同理,b是double类型,会强制转换为整型放到一个临时对象中,因此,它也需要const修饰。

​
int a = 10;
const int& ra = a*3; double b = 3.14;
const int& rb = b;    

8.5、引用和指针的关系

项目引用指针
初始化定义时必须初始化定义时不必须初始化
空间语法层面上将,引用不开辟空间需要开辟空间
修改定义初始化后不可更改被引用对象可以修改
访问直接访问必须解引用才能访问

 

九、inline

9.1、inline 的使用

inline 为关键字,用法是在函数定义时,把inline放在函数返回类型前。例如:

inline int Add(int x,int y)
{return x+y;
}

被 inline 修饰的函数就叫做内联函数。与普通函数不同的是,内联函数不在被调用的时候创建函数栈帧了,而是在编译的时候就展开为代码,像宏一样,这样可以提高效率

9.2、inline 的意义

inline 的目的是为了解决C语言中宏函数的问题,众所周知,C语言的宏函数体型小,但是非常容易出错,所以使用inline就可以代替宏函数,减少错误的发生

9.3、inline 的注意事项

前面提到,inline 函数是为了解决宏函数的,而宏函数的体型普遍很小,因此,内联函数适用于频繁调用的小函数。而且,inline 只是建议,如果函数主体太大,或者是递归函数,那么编译器会自动忽略inline

在VS编译器下,德不孤版本是默认不展开内联函数的,这样方便调试。

十、nullptr

C++中的nullptr是为了解决C语言中的NULL。在C语言中,NULL 既可以是整型 0 ,也可以是空指针((void*)0)。所以,很多时候会被搞混,因此,祖师爷引进了nullptr,它只表示指针型的 ((void*)0),避免了混淆

结语

恭喜你,成功阅读了这篇博客!从现在开始,你已经迈出了从C语言到C++的重要一步。也许你还只是刚刚接触了C++世界的冰山一角,但你已经了解了它与C语言的不同之处,以及面向对象编程的基本概念。

C++的学习之路漫漫,需要你不断实践、探索和总结。虽然刚开始可能会遇到一些挑战,但请相信,只要你坚持不懈,你一定能掌握这门强大的编程语言。

记住,享受编码的乐趣! 祝你在C++的世界里,创造出属于自己的精彩!

http://www.lryc.cn/news/606493.html

相关文章:

  • LCGL使用简介
  • 【qiankun】基于vite的qiankun微前端框架下,子应用的静态资源无法加载的问题
  • 详解Vite 配置中的代理功能
  • 基于岗位需求的康养休闲旅游服务实训室建设方案
  • 【赵渝强老师】OceanBase租户的资源管理
  • Opus音频编码器全解析:从技术原理到实战应用
  • 在 CentOS 7 安装中文字体
  • yolo目标检测基础知识
  • 【算法基础课-算法模板2】数据结构
  • 【Node】nvm在windows系统无管理员权限切换node版本
  • Vue3+Vite项目如何简单使用tsx
  • 【基于落霞归雁思维框架的软件项目管理实践指南】
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-53,(知识点:硬件电路问题排查,CPU上电后未运转,供电、时钟,复位,硬件连接)
  • 【Linux系列】SSD 与 HDD
  • Git之本地仓库管理
  • 尾插法和倒序输出
  • 【Keras学习笔记】开发环境搭建
  • pig Cloud中分布式锁的使用(setIfAbsent)
  • QT聊天项目DAY17
  • LeetCode 85:最大矩形
  • Shader开发(五)什么是渲染管线
  • Flutter兼容的iOS的最低版本号
  • 链特异性文库是什么?为什么它在转录组测序中越来越重要?
  • 【普中STM32精灵开发攻略】--第 2 章 开发板功能及使用介绍
  • 浅谈“压敏电阻”
  • 基于单片机智能油烟机设计/厨房排烟系统设计
  • 开发避坑短篇(12):达梦数据库TIMESTAMP字段日期区间查询实现方案
  • 快速搭建Java服务指南
  • 网站技术攻坚与Bug围剿手记
  • 环境配置·mmsegmentation和mmcv的安装