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

从C语言到C++:C++入门1

从C语言到C++:C++入门1

本系列旨在以C语言为基础,介绍C++的基本语法和特性。C语言中的基本语法包括变量、函数、循环、条件语句等,在C++中同样适用。C++在C语言的基础上,新增了一些特性,如类、对象、继承、多态等。

C++相较于C语言,最大的优势在于其对面向对象编程的支持,这使得C++更符合人类的思维模式,更符合实际问题的解决需求。下面,我们将以C语言开发中会遇到的具体问题为导向,逐步介绍C++的其他特性。

C++对于C语言的兼容

C++对于C语言的兼容性是其最大的优势之一。C++可以无缝地与C语言代码混合使用,这使得C++在开发中可以利用C语言的成熟库和工具,同时也可以利用C++的面向对象编程优势。

C++中使用C语言的语法输出"Hello World!":

#include<stdio.h>intmain(){printf("Hello World!\n");return0;}

C++中使用C++的语法输出"Hello World!":

#include<iostream>usingnamespacestd;intmain(){cout<<"Hello World!"<<endl;return0;}

上面两个代码都是简单的C++程序,它们都会打印出"Hello World!"。

对于一些已经熟悉C语言的开发者来说,C++的开发会更加方便,因为C++可以无缝地与C语言代码混合使用,同时也可以利用C++的面向对象编程优势。因此,我们在这里不在详细介绍C++的基础语法,而是直接进入C++的其他特性。

namespace命名空间

命名空间的定义

在使用C语言开发时,我们通常会遇到一些问题,如变量名冲突、函数名冲突等。

#include<stdio.h>#include<stdlib.h>intrand=10;intmain(){// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”printf("%d\n",rand);return0;}

命名冲突案例1:上面的代码中,我们定义了一个变量rand,但是rand函数在stdlib.h中已经存在,同时,他们都存在于全局域中,导致编译报错。

#include<stdio.h>intx=10;intmain(){intx=20;printf("%d\n",x);return0;}

命名冲突案例2:上面的代码中,main函数的局部域中存在一个变量x,而全局域中也存在一个变量x,局部域的x会隐藏全局域的x,最终打印出20。

案例1或许在日常开发中,可以通过修改变量名来解决大型项目,不同大型项目,不同模块之间的变量名冲突会更加严重,导致代码维护困难,特别是在有多个团队共同开发的情况下,每个团队可能会定义相同的变量名,导致代码冲突。

而案例2可能会导致代码的不可预测性,因为局部域的x会隐藏全局域的x,导致在main函数中打印出20,而不是10。

为了解决这些问题,C++引入了命名空间的概念。

命名空间是一个特殊的区域,用于存储变量、函数、类等。每个命名空间都有一个唯一的名称,用于区分不同的命名空间。

命名空间的语法如下:

namespacenamespace_name{// 命名空间中的代码}

命名空间的使用

命名空间的代码可以包含变量、函数、结构体、以及今后会学习到的类等。

namespaceexm-zem{// 变量intrand=10;// 函数intAdd(intleft,intright){returnleft+right;}// 结构体structNode{structNode*next;intdata;};}

命名空间的代码可以嵌套在其他命名空间中。

namespaceexm-zem{intrand=10;intAdd(intleft,intright){returnleft+right;}structNode{structNode*next;intdata;};// namespace嵌套定义namespaceC{intrand=1;}namespaceCPP{intrand=2;}}

使用命名空间中的变量、函数、结构体等时,需要使用**双冒号(::)**来访问。在嵌套命名空间中,需要使用多个双冒号(::)来访问。

#include<stdio.h>#include<stdlib.h>namespaceexm-zem{intrand=10;intAdd(intleft,intright){returnleft+right;}structNode{structNode*next;intdata;};// namespace嵌套定义namespaceC{intrand=1;}namespaceCPP{intrand=2;}}intmain(){printf("%d\n",exm-zem::rand);printf("%d\n",exm-zem::C::rand);printf("%d\n",exm-zem::CPP::rand);printf("%d\n",exm-zem::Add(1,2));return0;}

输出:

10 1 2 3

上面的代码中,我们定义了一个命名空间exm-zem,在其中定义了一个变量rand,一个函数Add,一个结构体Node,以及两个嵌套的命名空间CCPP。而在main函数中,我们使用双冒号(::)来访问exm-zem命名空间中的变量、函数、结构体等。

这样,我们在使用rand变量时,既不会和stdlib.h中的rand函数冲突,exm-zem和其中嵌套的CCPP命名空间中的rand变量之间也不会冲突。

命名空间的作用域

C++中有4中域:

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

双冒号就是用于指定所使用的域。当未指定域时(如未使用双冒号(::)时),与C语言相同,默认从局部域开始查找,如果未找到,再从全局域开始查找,不会进入命名空间域进行查找。当双冒号左侧置空时,表示从全局域开始查找。

#include<stdio.h>intx=10;intmain(){intx=20;printf("%d\n",x);printf("%d\n",::x);return0;}

输出:

20 10

命名空间的生命周期

在C语言中,对于一个变量、函数、结构体等,它的生命周期取决于它的作用域,在C++中也是如此。

对于命名空间来说,它只能在全局域中定义,因此它的生命周期与全局域相同。我们可以认为,命名空间是一个全局域的单独的子域,用于存储变量、函数、结构体等。

多文件命名空间

在多文件模块化编程中,每个文件都可以定义自己的命名空间,互不干扰。而同名的命名空间会被认为是同一个命名空间,不会冲突。

// file1.cpp#include<stdio.h>namespaceexm-zem{intrand1=10;}
// file2.cpp#include<stdio.h>namespaceexm-zem{intrand2=20;}

命名空间的展开

在C++中,当我们进行日常小练习或者进行算法竞赛时,很少会出现命名空间冲突的情况,因此我们会直接展开命名空间中的代码,而不是使用双冒号(::)来访问。

#include<iostream>// 标准输入输出流,今后会学习到usingnamespacestd;intmain(){cout<<"Hello World!"<<endl;// cout是标准输出流,endl是换行符,用于在输出后换行,<<是插入运算符,用于将数据插入到输入流中return0;}

在这段代码中,我们引入了很多新的概念,如标准输入输出流、endl、<<等。这些概念在今后的学习中会继续介绍。

std是标准命名空间,用于存储标准库中的函数、结构体等。所有C++提供的的标准库函数、结构体等,都被定义在std中,避免了命名空间冲突。

当然,如果你并不想把命名空间中的所有内容都展开,你可以只展开你需要的内容。

#include<iostream>usingstd::cout;intmain(){cout<<"Hello World!"<<std::endl;return0;}

只展开cout而不展开endl

初探IO流

在C语言中,我们使用printfscanf来实现输入输出。在C++中,我们使用coutcin来实现输入输出。

#include<iostream>usingstd::cout;usingstd::cin;usingstd::endl;intmain(){intx;cin>>x;cout<<x<<endl;return0;}

输入:

10

输出:

10

相较于C语言中的printfscanfcoutcin的使用更加方便,也更加安全。coutcin是标准输入输出流,而printfscanf是格式化输入输出函数,它们需要手动处理格式化问题,而coutcin则不需要手动处理格式化问题。

coutcin可以自动识别输入输出的数据类型,这也为之后我们将要学习到的类类型提供了更方便的输入输出操作。引入coutcin的本质目的,就是为了支持任意类型的输入输出操作。

#include<iostream>#include<string>usingnamespacestd;intmain(){string s;cin>>s;cout<<s<<endl;return0;}

输入:

Hello World!

输出:

Hello World!

这里我们用到了string类,用于存储字符串,之后我们也会详细介绍。cincout可以识别并输出字符串,而printfscanf则不能直接进行这样的操作。

缺省参数

在C++中,我们可以为函数的参数设置默认值,当调用函数时,如果没有提供该参数的值,就会使用默认值,这也被称为缺省参数。

#include<iostream>usingstd::cout;usingstd::cin;usingstd::endl;voidFunc(intx=10){cout<<x<<endl;}intmain(){Func();Func(20);return0;}

输出:

10 20

使用缺省参数时需要注意:

  1. 缺省参数必须是常量表达式,不能是变量。
  2. 缺省参数的顺序必须与函数的参数顺序一致。
  3. 缺省参数可以是函数的任意参数,包括非缺省参数。
  4. 设定缺省参数时,必须从右向左连续设定,不能从左向右设定。
  5. 传入参数时,必须从左向右连续传入,不能从右向左传入。

对于这上面的1-3条还是比较容易理解的,而4-5条我们来详细解析一下:

如果我们不遵守第四条:

#include<iostream>usingnamespacestd;voidFunc(intx=10,inty){cout<<x<<endl;cout<<y<<endl;}intmain(){Func(30);return0;}

那么传入的30究竟是传给x还是y?显然这会引起歧义,因此我们规定:设定缺省参数时,必须从右向左连续设定,不能从左向右设定。

同样,假设我们不遵守第五条:

#include<iostream>usingnamespacestd;voidFunc(intx=10,inty=20,intz=30){cout<<x<<endl;cout<<y<<endl;}intmain(){Func(,30,400);Func(30,,400);return0;}

这同样可能会引起歧义,30和400究竟是传给x、y还是z?因此我们规定:传入参数时,必须从左向右连续传入,不能从右向左传入。

正确设定和使用缺省参数的方式:

#include<iostream>usingnamespacestd;voidFunc(intx=10,inty=20,intz=30){cout<<x<<endl;cout<<y<<endl;cout<<z<<endl;}intmain(){Func1();Func1(1);Func1(1,2);Func1(1,2,3);return0;}

在进行C语言使用栈时,我们可能会遇到这两种情况:

  1. 已经确定了栈的大小。
  2. 不确定栈的大小。
// Stack.h#include<assert.h>#include<stdlib.h>typedefintSTDataType;typedefstructStack{STDataType*a;inttop;intcapacity;}ST;voidSTInit(ST*ps,intn);voidSTPush(ST*ps,intx);
// Stack.cpp#include"Stack.h"voidSTInit(ST*ps,intn){assert(ps&&n>0);ps->a=(STDataType*)malloc(4*sizeof(STDataType));// ... 扩容ps->top=0;ps->capacity=n;}voidSTPush(ST*ps,intx){//.. 此处省去具体代码}
// main.cpp#include"Stack.h"#include<iostream>#include"Stack.h"usingnamespacestd;intmain(){// 已经确定了有100项ST st1;STInit(&st1);for(size_t i=0;i<100;i++){STPush(&st1,i);}// 不确定有多少项ST st2;STInit(&st2);STPush(&st2,1);STPush(&st2,2);STPush(&st2,3);return0;}

当我们无法确定具体大小时,此时只能依靠扩容来满足需求。

而当我们已经可以确定具体大小时,在C语言当中还是只能默认初始化4个或指定的类型的大小,然后反复扩容,导致资源重复消耗。

C++引入了缺省参数,我们可以为函数的参数设置默认值,当调用函数时,如果没有提供该参数的值,就会使用默认值,这也被称为缺省参数。这样,我们既可以满足不确定有多少项的默认初始化4个类型大小,也可以满足这里的已经有确定大小而直接进行指定大小初始化的情况。

另外,在声明与定义函数分离时,缺省参数必须要在声明时指定,不能在定义时指定。需要缺省参数时,通过声明可以直接将缺省参数的值传递给函数,而不需要在调用时传递。

引入缺省参数后修改的程序

// Stack.h#include<assert.h>#include<stdlib.h>typedefintSTDataType;typedefstructStack{STDataType*a;inttop;intcapacity;}ST;voidSTInit(ST*ps,intn=4);// 可以使用缺省参数满足两种情况voidSTPush(ST*ps,intx);
// Stack.cpp#include"Stack.h"voidSTInit(ST*ps,intn){assert(ps&&n>0);ps->a=(STDataType*)malloc(n*sizeof(STDataType));// 这里可以根据n来初始化栈的大小// ...ps->top=0;ps->capacity=n;}voidSTPush(ST*ps,intx){//..}
// main.cpp#include<iostream>#include"Stack.h"usingnamespacestd;intmain(){// 已经确定了有100项ST st1;STInit(&st1,100);// 已经确定了栈的大小,在这里可以传入具体大小for(size_t i=0;i<100;i++){STPush(&st1,i);}// 不确定有多少项ST st2;STInit(&st2);// 不确定栈的大小,在这里可以使用缺省参数4STPush(&st2,1);STPush(&st2,2);STPush(&st2,3);return0;}

本章完

http://www.jsqmd.com/news/1214507/

相关文章:

  • 机器人自动化生产线的维护复杂吗?
  • 2026泸州别墅自建施工怎么选?TOP 砖混 + 框架 + 轻钢 + 庭院一体化 附电话地址 - 中检检测集团
  • 2026济南LV高价回收|老花法棍onthego当场打款正规门店排行 - 商业每日快报
  • 2026 年 7 月北京萧邦纳沙泰尔售后地址导航,官方腕表维修养护中心 - 萧邦官方维修中心
  • 外贸社交媒体营销与 B2B 外贸获客培训优选:东莞市华创网络优质服务商实力稳居行业前茅 - 优企甄选
  • 欧米茄北京售后维修服务中心|北京欧米茄授权维修中心联系电话 + 统一客服 400-883-8097 权威信息公告(2026 年 7 月最新) - 欧米茄中国售后中心
  • 一站式全屋 / 商铺智慧用电管控平台|炜晔 WYA1-4TL 多场景落地解决方案
  • 2026济南黄金回收白银回收铂金回收中检持证鉴定师铂金银饰高价回收门店联系方式推荐
  • AI时代,为什么泛知识能力正在变得越来越重要
  • BBPlayer 可以听的B站HiRes Bilibili无损音频播放器 B站链接解析安卓听歌APP
  • 2026绍兴别墅自建施工怎么选?TOP 砖混 + 框架 + 轻钢 + 庭院一体化 附电话地址 - 中检检测集团
  • 2026开封市民高频选择的5家实体水质检测饮用水检测井水检测自来水检测生活污水废水检测第三方实地测评整理 - 一修哥咨询
  • 惠州秋长DHL/FedEx/UPS国际快递代理哪家好13500091568 - 烟雾弥漫L
  • 上海阿里云代理商典名科技:企业上云的专业伙伴与价值桥梁
  • 国产大模型哪家强?深度评测文心一言4.5、通义千问Qwen2.5、混元HunYuan、Kimi+GLM-4五大主力模型(附私有化部署避坑清单)
  • PKCS#11认识(二)—Object 与 Attribute
  • 2026年国内热门木工五轴雕刻机靠谱销售厂家盘点及选购参考
  • 2026邯郸别墅自建施工怎么选?TOP 砖混 + 框架 + 轻钢 + 庭院一体化 附电话地址 - 中检检测集团
  • BI工具选型误区:为什么你的数据看板总是“用不起来”?
  • 2026眉山泳池高危检测怎么选?高危检测机构TOP 办证 + 年审 + 水质全项 附电话地址 - 鉴安检测
  • 天津市河东区知名的公司注销财税公司哪家专业
  • SpringAOP+Logback+MDC实现traceId全链路日志追踪
  • Unity弧形文本实现:从顶点变换到性能优化的完整指南
  • 抖音视频怎么去水印?
  • 2026黄南别墅自建施工怎么选?TOP 砖混 + 框架 + 轻钢 + 庭院一体化 附电话地址 - 中检检测集团
  • 2026聊城雨棚搭建怎么选?TOP 铝合金 + 玻璃 + 耐力板 + 上门测量安装 附电话地址 - 科信检测
  • 天翼爱音乐AI歌手“小辰”:以乐为舟,伴每一个逐光前行的你
  • 北京亦庄宠物肝肾养护诊疗哪家好
  • 上海防水补漏公司推荐(2026.7月)本地正规服务商 - 吉林同城获客
  • 为什么你的Cursor总生成无效断言?揭秘LLM理解业务逻辑的4个隐性前提与修复公式