C++作为一门高效且功能强大的编程语言,自诞生以来一直是系统开发、游戏引擎、高性能计算等领域的核心工具。其独特的面向对象特性、底层内存控制能力以及丰富的标准库支持,使得开发者能够灵活地平衡性能与抽象需求。对于初学者而言,掌握C++不仅意味着理解语法规则,更需要培养对计算机系统底层运作的认知思维。本文将从基础语法、核心概念到实际应用场景,系统性拆解C++的学习路径,帮助读者建立扎实的编程基础,同时规避常见的内存管理陷阱和性能误区。下面就让我们正式开始吧!
C++ 的起源可追溯至 1979 年,当时 Bjarne Stroustrup(本贾尼・斯特劳斯特卢普,译名存在地域差异)在贝尔实验室从事计算机科学与软件工程研究。面对模拟开发、操作系统开发等复杂项目需求,他发现 C 语言等现有工具在表达能力、可维护性、可扩展性上存在明显不足。
1983 年,Stroustrup 在 C 语言基础上引入面向对象编程特性,设计出 C++ 雏形。此时的 C++ 已具备类、封装、继承等核心概念,为后续面向对象编程发展奠定基础,该语言也在这一年正式定名 “C++”。
此后数年,C++ 在学术界与工业界的应用逐步普及:
C++ 的标准化工作始于 1989 年,ANSI(美国国家标准协会)与 ISO(国际标准化组织)联合成立标准化委员会。1994 年,委员会推出首个标准化草案,在保留 Stroustrup 最初定义的全部特征基础上,新增了部分功能。
值得注意的是,首个草案完成后,委员会投票通过将STL(标准模板库) 纳入 C++ 标准。STL 由 Alexander Stepanov、Meng Lee、David R Musser 在惠普实验室开发,其功能扩展超出了 C++ 最初定义范围。这一决定虽对 C++ 意义重大,但也延缓了标准化进程。
最终,联合标准化委员会于 1997 年 11 月 14 日通过标准最终草案,并在 1998 年正式发布 ANSI/ISO C++ 标准,标志着 C++ 进入标准化应用阶段。




C++⼀直被诟病的⼀个地⽅就是⼀直没出网络库(networking),networking之前是在C++23的计划中的,现在C++23已经发布了,但是没有networking,于是网上就引发了⼀系列的吃瓜和吐槽。中间过程就像发生了宫斗剧⼀样。




我在下面给出C++的三个参考文档:
https://legacy.cplusplus.com/reference/
https://en.cppreference.com/w/cpp.html
https://en.cppreference.com/w/
说明:在第一个链接当中的不是C++的官方文档,标准也只更新到C++11,但是以头文件的形式呈现,内容是比较易懂的。后面两个链接分别是C++官方文档的中文版和英文版,信息是比较全的,更新到了最新的C++标准,但是相比于第一个不那么易看;这几个文档是各有优势的,我们在学习过程中可以结合着使用。
TIOBE排⾏榜是根据互联⽹上有经验的程序员、课程和第三方厂商的数量,并使用搜索引擎(如Google、Bing、Yahoo!)以及Wikipedia、Amazon、YouTube和Baidu(百度)统计出排名数据,只是反映某个编程语⾔的热门程度,并不能说明一门编程语⾔好不好,或者一门语言所编写的代码数量多少。
2024年6月TIOBE发布的编程语言排行榜


可以看到,近几年来C++还是稳定在榜单第二的位置的,可以证明C++的地位之高。
C++的应用领域有很多,服务器端、游戏(引擎)、机器学习引擎、音视频处理、嵌入式软件、电信设备、金融应用、基础库、操作系统、基础架构、基础工具、硬件交互等多个方面都有。
1. 大型系统软件开发。如编译器、数据库、操作系统、浏览器等等。
2. 音视频处理。常见的音视频开源库和方案有FFmpeg、WebRTC、Mediasoup、ijkplayer,y音视频开发最主要的技术栈就是C++。
3. PC客户端开发。一般是开发Windows上的桌面软件,比如WPS之类的,技术栈的话⼀般是C++和QT,QT是跨平台的一个C++图形用户界面(Graphical User Interface,GUI)程序。
4. 服务端开发。各种大型应用网络连接的高并发后台服务。在这个领域Java使用的也比较多,C++主要⽤于⼀些对性能要求比较高的地方。如:游戏服务、流媒体服务、量化高频交易服务等。
5. 游戏引擎开发。现在市面上的很多游戏引擎都是使用C++开发的。游戏开发需要掌握C++基础和数据结构,学习图形学知识,掌握游戏引擎和框架,了解引擎实现,引擎源代码可以学习UE4(虚幻四)、Cocos2d-x等开源的引擎来实现。
6. 嵌入式开发。嵌入式把具有计算能力的主控板嵌入到机器装置或者电子装置的内部,通过软件能够控制这些装置。比如:智能⼿环、摄像头、扫地机器人、智能音响、门禁系统、车载系统等等,粗略一点来说,嵌入式开发主要分为嵌入式应用和嵌入式驱动开发。
7. 机器学习引擎。机器学习底层的很多算法都是使用C++实现的,上层再使用Python封装起来。如果只想准备数据训练模型,那么学会Python基本上是够用的,但是如果想要做机器学习系统的开发,那么就必须要学C++。
8. 测试开发/测试。每个公司的研发团队中,只要有研发人员就一定有测试人员。测试主要分为测试开发和功能测试,测试开发一般是使用一些工具(如selenium、Jmeter等),设计测试用例,然后再写一些脚本进行自动化测试、性能测试等。有些还需要自行开发一些测试工具(比如嵌入式开发领域有时需要利用C++开发串口调试工具等)。功能测试主要是根据产品的功能,设计测试用例,然后再通过手动的方式进行调试。



C++ Primer:主要讲语法,经典的语法书籍,学习的前中后期都可以看,前期学习时如果是自学 的话看可能会有点晦涩难懂,中后期可作为语法字典,非常好用。 STL源码剖析:主要从底层实现的角度结合STL源码,庖丁解牛式地剖析STL的实现,是侯捷老师的经典之作。这本书可以很好地帮助我们学习别人是如何实现出高效简洁的数据结构和算法代码的,以及如何使用泛型封装等。 Effctive C++:这本书也是侯捷老师翻译的,它主要讲解了55个如何正确⾼效使⽤C++的条款,中后期可以看一遍。
C++是兼容C语言绝对大多数的语法的,所以C语言实现的hello world程序依旧是可以在C++文件中运行的。C++中需要把定义文件代码后缀改为.cpp,Visio Studio编译器看到是.cpp文件就会自动调用C++编译器来对代码进行编译,在linux环境下就需要使用g++进行编译了,而不再是gcc。如下所示:
// test.cpp
#include<stdio.h>
int main()
{
printf("hello world\n");
return 0;
}当然,C++也有一套自己的输入输出格式,严格来说C++版本的hello world应该是这样写的:
// test.cpp
// 这⾥的std cout等我们都看不懂,没关系,下⾯我们会依次讲解
#include<iostream>
using namespace std;
int main()
{
cout << "hello world\n" << endl;
return 0;
}在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称都将存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,从而避免命名上的冲突或名字的污染,namespace关键字的出现就是用于解决这种问题的。
C语言项目中,类似下面程序这样的命名冲突是普遍存在的问题:
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{
// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”
printf("%d\n", rand);
return 0;
}上述这些定义我们可以通过下面的代码来理解一下:
#include <stdio.h>
#include <stdlib.h>
// 1. 正常的命名空间定义
// bit是命名空间的名字,⼀般开发中是⽤项⽬名字做命名空间名。
// 我在这里⽤的是bit,⼤家可以考虑⽤⾃⼰名字缩写,如张三:
namespace bit
{
// 命名空间中可以定义变量/函数/类型
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);
// 这⾥指定bit命名空间中的rand
printf("%d\n", bit::rand);
return 0;
}
//2. 命名空间可以嵌套
namespace bit
{
namespace op
{
int rand = 1;
int Add(int left, int right)
{
return left + right;
}
}
namespace chen
{
int rand = 2;
int Add(int left, int right)
{
return (left + right)*10;
}
}
}
int main()
{
printf("%d\n", bit::pg::rand);
printf("%d\n", bit::hg::rand);
printf("%d\n", bit::pg::Add(1, 2));
printf("%d\n", bit::hg::Add(1, 2));
return 0;
}
// 多⽂件中可以定义同名namespace,他们会默认合并到⼀起,就像同⼀个namespace⼀样
// Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
namespace bit
{
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
void STInit(ST* ps, int n);
void STDestroy(ST* ps);
void STPush(ST* ps, STDataType x);
void STPop(ST* ps);
STDataType STTop(ST* ps);
int STSize(ST* ps);
bool STEmpty(ST* ps);
}
// Stack.cpp
#include"Stack.h"
namespace bit
{
void STInit(ST* ps, int n)
{
assert(ps);
ps->a = (STDataType*)malloc(n * sizeof(STDataType));
ps->top = 0;
ps->capacity = n;
}
// 栈顶
void STPush(ST* ps, STDataType x)
{
assert(ps);
// 满了, 扩容
if (ps->top == ps->capacity)
{
printf("扩容\n");
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = (STDataType*)realloc(ps->a, newcapacity *sizeof(STDataType));
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->a = tmp;
ps->capacity = newcapacity;
}
ps->a[ps->top] = x;
ps->top++;
}
//...
}
// Queue.h
#pragma once
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
namespace bit
{
typedef int QDataType;
typedef struct QueueNode
{
int val;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* phead;
QNode* ptail;
int size;
}Queue;
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
// ⼊队列
void QueuePush(Queue* pq, QDataType x);
// 出队列
void QueuePop(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
}
// Queue.cpp
#include"Queue.h"
namespace bit
{
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = NULL;
pq->ptail = NULL;
pq->size = 0;
}
// ...
}
/
/ test.cpp
#include"Queue.h"
#include"Stack.h"
// 全局定义了⼀份单独的Stack
typedef struct Stack
{
int a[10];
int top;
}ST;
void STInit(ST* ps){}
void STPush(ST* ps, int x){}
int main()
{
// 调⽤全局的
ST st1;
STInit(&st1);
STPush(&st1, 1);
STPush(&st1, 2);
printf("%d\n", sizeof(st1));
// 调⽤bit namespace的
bit::ST st2;
printf("%d\n", sizeof(st2));
bit::STInit(&st2);
bit::STPush(&st2, 1);
bit::STPush(&st2, 2);
return 0;
}编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,并不会到命名空间里去查找。所以下面的程序会编译报错。因此我们要使用命名空间中定义的变量或函数。有三种方式:
如下代码所示:
#include<stdio.h>
namespace bit
{
int a = 0;
int b = 1;
}
int main()
{
// 编译报错:error C2065: “a”: 未声明的标识符
printf("%d\n", a);
return 0;
}
// 指定命名空间访问
int main()
{
printf("%d\n", N::a);
return 0;
}
// using将命名空间中某个成员展开
using N::b;
int main()
{
printf("%d\n", N::a);
printf("%d\n", b);
return 0;
}
// 展开命名空间中全部成员
using namespce N;
int main()
{
printf("%d\n", a);
printf("%d\n", b);
return 0;
}本期博客是C++的第一期博客,博主为大家简单介绍了一下C++的历史和一部分基础知识,下期博客将继续介绍C++的入门基础知识,请大家多多支持!