文章目录
前言
下篇已出:【C++篇】迈入新世界的大门------初识C++(下篇)
C++发展历史
C++起源
与C语言一样,C++也是在贝尔实验室诞生的,Bjarne Stroustrup于20世纪80年代在这里开发出了这种语言。Stroustrup比较关系的是让C++更有用,而不是实施特定的编程原理和风格。名称C++来自C语言的递增运算符++,名称C++表示它是C的扩充版本。
祖师爷
C++版本更新
C++23小故事
C++⼀直被诟病的⼀个地⽅就是⼀直没出⽹络库( networking ), networking之前是在C++23的计划中
的,现在C++23已经发布了,但是没有networking,⽹上引发了⼀系列的吃⽠和吐槽。中间过程就
像发⽣了宫⽃剧⼀样。
P2452R0 2021 October Library Evolution and Concurrency Networking and Executors Poll
Outcomes
C++23的⽬标
C++在工作领域的应用
C++的应⽤领域服务器端、游戏(引擎)、机器学习引擎、⾳视频处理、嵌⼊式软件、电信设备、⾦融 应⽤、基础库、操作系统、编译器、基础架构、基础⼯具、硬件交互等很多⽅⾯都有。
- ⼤型系统软件开发。如编译器、数据库、操作系统、浏览器等等
- ⾳视频处理。常⻅的⾳视频开源库和⽅案有FFmpeg、WebRTC、Mediasoup、ijkplayer,⾳视频
开发最主要的技术栈就是C++。 - PC客⼾端开发。⼀般是开发Windows上的桌⾯软件,⽐如WPS之类的,技术栈的话⼀般是C++和
QT,QT 是⼀个跨平台的 C++图形⽤⼾界⾯(Graphical User Interface,GUI)程序。 - 服务端开发。各种⼤型应⽤⽹络连接的⾼并发后台服务。这块Java也⽐较多,C++主要⽤于⼀些对
性能要求⽐较⾼的地⽅。如:游戏服务、流媒体服务、量化⾼频交易服务等 - 游戏引擎开发。很多游戏引擎就都是使⽤C++开发的,游戏开发要掌握C++基础和数据结构,学习
图形学知识,掌握游戏引擎和框架,了解引擎实现,引擎源代码可以学习UE4、Cocos2d-x等开源
引擎实现 - 嵌⼊式开发。嵌⼊式把具有计算能⼒的主控板嵌⼊到机器装置或者电⼦装置的内部,通过软件能够
控制这些装置。⽐如:智能⼿环、摄像头、扫地机器⼈、智能⾳响、⻔禁系统、⻋载系统等等,粗
略⼀点,嵌⼊式开发主要分为嵌⼊式应⽤和嵌⼊式驱动开发。 - 机器学习引擎。机器学习底层的很多算法都是⽤C++实现的,上层⽤python封装起来。如果你只想
准备数据训练模型,那么学会Python基本上就够了,如果你想做机器学习系统的开发,那么需要学
会C++。 - 测试开发/测试。每个公司研发团队,有研发就有测试,测试主要分为测试开发和功能测试,测试
开发⼀般是使⽤⼀些测试⼯具(selenium、Jmeter等),设计测试⽤例,然后写⼀些脚本进⾏⾃动化
测试,性能测试等,有些还需要⾃⾏开发⼀些测试⽤具。功能测试主要是根据产品的功能,设计测
试⽤例,然后⼿动的⽅式进⾏测试。
C++参考网站及文档书籍
编程语言排行榜
TIOBE排⾏榜是根据互联⽹上有经验的程序员、课程和第三⽅⼚商的数量,并使⽤搜索引擎(如
Google、Bing、Yahoo!)以及Wikipedia、Amazon、YouTube和Baidu(百度)统计出排名数据,只 是反映某个编程语⾔的热⻔程度,并不能说明⼀⻔编程语⾔好不好,或者⼀⻔语⾔所编写的代码数量 多少。
TIOBLE
2024年8月排行榜
可见C/C++占比很高!
趋势
C++难度
C++是⼀个相对难学难精的语⾔,相⽐其他⼀些语⾔,学习难度要⾼⼀些要陡峭⼀些,这⾥有历史包袱的问题,也有语⾔本⾝设计和发展历史的问题。 ⽹上以前⼀直流传下⾯这个21天内⾃学精通C++的梗。当然⾃学难度很⼤,跟着⽐特课程⾛,相对会好⼀些。
参考文档书籍
参考文档
https://legacy.cplusplus.com/reference/
https://zh.cppreference.com/w/cpp
https://en.cppreference.com/w/
参考书籍
》C++ Primer:主要讲解语法,经典的语法书籍,前后中期都可以看,前期如果⾃学看可能会有点晦涩 难懂,能看懂多少看懂多少,就当预习,学了⽐特课程后,中后期作为语法字典,⾮常好⽤。
》STL源码剖析:主要从底层实现的⻆度结合STL源码,庖丁解⽜式剖析STL的实现,是侯捷⽼师的经典 之作。可以很好的帮助我们学习别⼈⽤语法是如何实现出⾼效简洁的数据结构和算法代码如何使⽤ 泛型封装等。让我们不再坐井观天,闭⻔造⻋,本书课程上⼀半以后,中后期可以看。
》Effctive C++:本书也是侯捷⽼师翻译的,本书有的⼀句评价,把C++程序员分为看过此书的和没看过 此书的。本书主要讲了55个如何正确⾼效使⽤C++的条款,建议中后期可以看⼀遍,⼯作1-2年后再看 ⼀遍,相信会有不⼀样的收获。
C++第一个程序
C++兼容C语⾔绝⼤多数的语法,所以C语⾔实现的hello world依旧可以运⾏,C++中需要把定义⽂件 代码后缀改为.cpp,vs编译器看到是.cpp就会调⽤C++编译器编译,linux下要⽤g++编译,不再是gcc
//test.cpp
#include<stdio.h>
int main()
{
printf("hello world!");
return 0;
}
C++有自己一套严格的输入输出!
//test.cpp
#include<iostream>
using namespace std;
int main()
{
count<<"hello world\n"!<<endl;
return 0;
}
命名空间
为什么使用namespace
在C/C++中,变量、函数和后⾯要学到的类都是⼤量存在的,这些变量、函数和类的名称将都存在于全 局作⽤域中,可能会导致很多冲突。使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名 冲突或名字污染,于是祖师爷针对此问题在C/C++引入namespace
#include<stdio.h>
#include<stdlib.h>
int rand = 20;
int main()
{
//编译报错::"rand"重定义:以前定义的是"函数"
printf("%d\n", rand);
return 0;
}
因为标准库中有rand这个函数,在预处理阶段把头文件拷贝过来,在全局域就有两个rand,所以编译器认为rand"重定义"
namesapce定义及规则
• 定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
namespace myTest
{
int rand = 20;
int add()
{
//......
}
}
• namespace本质是定义出⼀个域,这个域跟全局域各⾃独⽴,不同的域可以定义同名变量,所以下 ⾯的rand不在冲突了。
namespace myTest
{
// 命名空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int main()
{
// 这⾥默认是访问的是全局的rand函数指针
printf("%p\n", rand);
// 这⾥指定myTest命名空间中的rand
printf("%d\n", myTest::rand);
return 0;
}
• C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/
类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响
编译查找逻辑,还会影响变量的⽣命周期,命名空间域和类域不影响变量⽣命周期。
• namespace只能定义在全局,当然他还可以嵌套定义。
namespace bit
{
// xiaohua
namespace xiaohua
{
int rand = 1;
int Add(int left, int right)
{
return left + right;
}
}
// xiaopang
namespace xiaopang
{
int rand = 2;
int Add(int left, int right)
{
return (left + right) * 10;
}
}
}
int main()
{
printf("%d\n", bit::xiaohua::rand);
printf("%d\n", bit::xiaopang::rand);
printf("%d\n", bit::xiaohua::Add(1, 2));
printf("%d\n", bit::xiaopang::Add(1, 2));
return 0;
}
bu
• 项⽬⼯程中多⽂件中定义的同名namespace会认为是⼀个namespace,不会冲突。
• C++标准库都放在⼀个叫std(standard)的命名空间中。
#include<iostream>
using namespace std;
int main()
{
cout << "hello world" << endl;
return 0;
}
命名空间使用
编译查找一个变量的声明/定义时,默认只会在局部或全局查找,不会到命名空间查找。所以我们要使用命名空间中定义的变量/函数,有三种方式:
不指定就会报错
#include<iostream>
namespace mytest
{
int a = 0;
int b = 0;
}
int main()
{
//error C2065: "a": 未声明的标识符
printf("%d", a);
}
• 指定命名空间访问,项⽬中推荐这种⽅式。
• using将命名空间中某个成员展开,项⽬中经常访问的不存在冲突的成员推荐这种⽅式。
• 展开命名空间中全部成员,项⽬不推荐,冲突⻛险很⼤,⽇常⼩练习程序为了⽅便推荐使⽤。
制定命名空间使用
int main()
{
printf("%d",mytest:: a);
}
using将命名空间中某个成员展开
using mytest::a;
int main()
{
printf("%d", a);
}
展开命名空间中全部成员
namespace mytest
{
int a = 0;
int b = 0;
}
using namespace mytest;
int main()
{
printf("%d", a);
}
C++输入&输出
• <iostream> 是 Input Output Stream 的缩写,是标准的输⼊、输出流库,定义了标准的输⼊、输
出对象。
• std::cin 是 istream 类的对象,它主要⾯向窄字符(narrow characters (of type char))的标准输
⼊流。
• std::cout 是 ostream 类的对象,它主要⾯向窄字符的标准输出流。
• std::endl 是⼀个函数,流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区。
• <<是流插⼊运算符,>>是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)
• 使⽤C++输⼊输出更⽅便,不需要像printf/scanf输⼊输出时那样,需要⼿动指定格式,C++的输⼊ 输出可以⾃动识别变量类型(本质是通过函数重载实现的,这个以后博客会讲到),其实最重要的是C++的流能更好的⽀持⾃定义类型对象的输⼊输出。
#include<iostream>
using namespace std;
int main()
{
int a = 20;
double d = 0.1;
char c = 'x';
cout << a << "" << d << "" << c;
scanf("%d%f", &a, &d);
printf("%d%f", a, d);
//可以自动识别变量类型
cin >> a;
cin >> d >> c;
cout << a << endl;
cout << d << c << endl;
return 0;
}
• IO流涉及类和对象,运算符重载、继承等很多⾯向对象的知识,这些知识我们还没有讲解,所以这⾥我们只能简单认识⼀下C++ IO流的⽤法,后⾯我们会有专⻔的⼀个章节来细节IO流库。
• cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间中,所以要
通过命名空间的使⽤⽅式去⽤他们。
• ⼀般⽇常练习中我们可以using namespace std,实际项⽬开发中不建议using namespace std。
• 这⾥我们没有包含<stdio.h>,也可以使⽤printf和scanf,在包含<iostream>间接包含了。vs系列
编译器是这样的,其他编译器可能会报错。
补充:
include <iostream>
using namespace std;
int main ()
{
// 在 io 需求⽐较⾼的地⽅,如部分⼤量输⼊的竞赛题中,加上以下 3 ⾏代码
// 可以提⾼ C++IO 效率
ios_base:: sync_with_stdio ( false );
cin. tie ( nullptr );
cout. tie ( nullptr );
return 0 ;
}IO流机类和对象,运算符重载、继承等很多面向对象的知识,这些知识我们还没讲解,所以这里我们只简单认识一下C++IO流的用法,后面会有专门的一个章节细节讲解IO流库。
缺省函数
• 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参 则采⽤该形参的缺省值,否则使⽤指定的实参,缺省参数分为全缺省和半缺省参数。(有些地⽅把 缺省参数也叫默认参数)
include <iostream>
include <assert.h>
using namespace std;
void Func ( int a = 0 )
{
cout << a << endl;
}
int main ()
{
Func (); // 没有传参时,使⽤参数的默认值
Func ( 10 ); // 传参时,使⽤指定的实参
return 0 ;
}
• 全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。
• 带缺省参数的函数调⽤,C++规定必须从左到右依次给实参,不能跳跃给实参。include <iostream>
using namespace std;
// 全缺省
void Func1 ( int a = 10 , int b = 20 , int c = 30 )
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
// 半缺省
void Func2 ( int a, int b = 10 , int c = 20 )
{
cout << "a = " << a << endl;
cout << "b = " << b << endl;
cout << "c = " << c << endl << endl;
}
int main ()
{
Func1 ();
Func1 ( 1 );
Func1 ( 1 , 2 );
Func1 ( 1 , 2 , 3 );
Func2 ( 100 );
Func2 ( 100 , 200 );
Func2 ( 100 , 200 , 300 );
return 0 ;
}
• 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省
值。
// Stack.h
void STInit (ST* ps, int n = 4 );
// 缺省参数不能声明和定义同时给
void STInit (ST* ps, int n)
{
assert (ps && n > 0 );
ps->a = (STDataType*) malloc (n * sizeof (STDataType));
ps->top = 0 ;
ps->capacity = n;
}
函数重载
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;
}
返回值不能作为重载条件,因为调用时也无法区分。
返回值不同不能作为重载条件,因为调⽤时也⽆法区分
//void fxx()
//{}
//
//int fxx()
//{
// return 0;
//}
注意:
// 下⾯两个函数构成重载
// f() 但是调⽤时,会报错,存在歧义,编译器不知道调⽤谁
void f1 ()
{
cout << "f()" << endl;
}
void f1 ( int a = 10 )
{
cout << "f(int a)" << endl;
}
int main ()
{
Add ( 10 , 20 );
Add ( 10.1 , 20.2 );
f ();
f ( 10 );
f ( 10 , 'a' );
f ( 'a' , 10 );
return 0 ;
}
通过函数重载,C++函数调用就表现了多态行为,使用更灵活。C语言不支持同一作用中出现同名函数的。
你的支持就是我创作的动力!