在C++系列博客正式开始之前,我认为有必要了解一下关于C++的一些发展历程以及一些比较细碎但重要的C++和C语言的差异。
C++的起源可以追溯到1979年,当时Bjarne Stroustrup(本贾尼·斯特劳斯特卢普)在贝尔实验室从事计算机科学和软件工程的研究工作。面对项目中复杂的软件开发任务,特别是模拟和操作系统的开发工作,他感受到了现有语言(如C语言)在表达能力、可维护性和可扩展性方面的不足。 1983年,Bjarne Stroustrup在C语言的基础上添加了面向对象编程的特性,设计出了C++语言的雏形此时的C++已经有了类、封装、继承等核心概念,为后来的面向对象编程奠定了基础。这一年该语言被正式命名为C++。 在随后的几年中,C++在学术界和工业界的应用逐渐增多。一些大学和研究所开始将C++作为教学和研究的首选语言,而一些公司也开始在产品开发中尝试使用C++。这一时期,C++的标准库和模板等特性也得到了进一步的完善和发展。 C++的标准化工作于1989年开始,并成立了一个ANSI和IS0(International StandardsOrganization)国际标准化组织的联合标准化委员会。1994年标准化委员会提出了第一个标准化草案。在该草案中,委员会在保持斯特劳斯特卢普最初定义的所有特征的时,还增加了部分新特征。 在完成C++标准化的第一个草案后不久,STL(Standard Template Library)诞生了,STL是惠普实验室开发的一系软件的统称。它是由Alexander Stepanov、MengLee和David RMusser在惠普实验室工作时所开发出来的。在通过了标准化第一个草案之后,联合标准化委员会又投票并通过了将STL包含到C++标准中的提议。STL对C++的扩展超出C++的最初定义范围。虽然在标准中增加STL是个很重要的决定,但也因此延缓了C++标准化的进程。 1997年11月14日,联合标准化委员会通过了该标准的最终草案。1998年,C++的ANSI/IS0标准被投入使用。
图为本贾尼博士
版本 | 特性概览 |
---|---|
C++98 (1998) | 第一个标准化的C++版本。 |
C++03 (2003) | 修订了C++98标准,增加了一些新特性,如可选的具体模板实例化 (SSP)。 |
C++11 (2011) | 引入了许多新特性,如列表初始化、自动类型推导、右值引用、lambda表达式、智能指针等。 |
C++14 (2014) | 在C++11基础上进一步增加了一些新特性,主要是对C++11的小修小补。 |
C++17 (2017) | 引入了很多新特性,如inline变量、fold表达式、if和switch控制语句中的初始化器等。 |
C++20 (2020) | 引入了许多重要的新特性,如范围for循环、模块、协程、原始字符串字面量等。 |
C++一直被诟病的一个地方就是没有网络库(networking,用于网络通信,使用频率极高),networking
之前是在C++23的计划中的,但是现在C++23发布之后却没有networking
,在网上引发了一系列的吐槽。
过程确实很精彩,但很遗憾结果就是networking
并没有出现在C++23上,C++作为面向底层的语言,实际生产中使用的版本迭代速度是远远慢于新标准制定的,所以想正式使用networking
,恐怕还有很长一段时间。
TIOBE排行榜是根据互联网上有经验的程序员、课程和第三方厂商的数量,并使用搜索引擎(如Google、Bing、Yahoo!)以及Wikipedia、Amazon、YouTube和Baidu(百度)统计出排名数据,只是反映某个编程语言的热门程度,并不能说明一门编程语言好不好,或者一门语言所编写的代码数量多少。 2024年9月TIOBE发布的编程语言排行榜
可以看到C++具有十分高的热度,目前排在所有编程语言中的第二名。 当然这个榜单的可信度并不强,在之前很长的一段时间中,C语言一直都是高于C++的,但现在C语言却以不小的劣势掉出了前三。看看就好,不必较真。
C++在领域服务器端、游戏(引擎)、机器学习引擎、音视频处理、嵌入式软件、电信设备、金融应用、基础库、操作系统、编译器、基础架构、基础工具、硬件交互等很多方面都有应用。
首先第一个问题,C++难学吗? 毋庸置疑地说,C++是一个相对难学难精的语言,相比其他一些语言,学习难度要高一些要陡峭一些,这里有历史包袱的问题,也有语言本身设计和发展历史的问题。网上以前流传着下面这个21天内自学精通C++的梗。
尽管C++的学习难度不算小,但我会竭尽全力把每一篇博客写好,如果你刚刚开始学习C++,不妨关注我,我会持续带来更多的优质博客。
C++ Primer:主要讲解语法,经典的语法书籍,前后中期都可以看,前期如果自学看可能会有点晦涩难懂,能看懂多少看懂多少,中后期可以作为语法字典,非常好用。
STL源码剖析:主要从底层实现的角度庖丁解牛式地剖析STL的实现,是侯捷老师的经典之作。可以很好的帮助我们学习别人用语法是如何实现出高效简洁的数据结构和算法代码,如何使用泛型封装等。可以让我们不再坐井观天,闭门造车,中后期可以看。
Effctive C++:本书也是侯捷老师翻译的,本书有的一句评价,可以把C++程序员分为看过此书的和没看过此书的。本书主要讲了55个如何正确高效使用C++的条款,建议中后期可以看一遍,工作1-2年后再看一遍,相信会有不一样的收获。
好的,那么关于C++的一些基础情况就说到这里,下面开始正式的入门基础知识。
C++兼容C语言绝大多数的语法,所以C语言实现的hello world
依旧可以运行,C++文件需要把文件后缀改为.cpp
,编译器看到是.cpp
就会调用C++编译器编译,Linux下要用g++编译,不再是gcc。
你当然可以完全使用C语言的语法去实现第一个C++程序Hello World,但是严格地说,纯C++版本的Hello Word!
应该是这样写的:
#include<iostream>
using namespace std;
int main()
{
cout << "Hello World!" << endl;
return 0;
}
或是
#include<iostream>
int main()
{
std::cout << "Hello World!" << std::endl;
return 0;
}
二者是等价的。正如当初初学C语言时看不懂Hello World
程序一样,这个你多半也看不明白,本文接下来的内容就会将这个程序解释清楚。
在C/C++中,变量、函数和后面讲到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。
c语言项目出现类似下面的程序的命名冲突是普遍存在的问题,C++引入namespace
就是为了解决这样的问题,使用命名空间的可以对标识符的名称进行本地化,以避免命名冲突或名字污染。
#include<stdio.h>
#include<stdlib.h> //这个头文件中包含rand()函数
int rand = 0;
int main()
{
printf("%d", rand);
return 0;
}
这个代码中由于全局变量int
与stdlib.h
中的rand()
函数命名冲突,所以编译器会给出如下报错:
namespace
关键字,后面跟命名空间的名字,然后接一对{}
即可,{}
中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。namespace
本质是定义出一个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand
不再冲突了。//创建一个命名空间使 rand变量不与 rand函数冲突
#include<stdio.h>
#include<stdlib.h>
namespace fhvyxyci
{
//变量
int rand = 10;
//函数
int add(int a, int b)
{
return a + b;
}
//类型,以struct结构体举例,尽管它在C++中有更好的上位替代
struct fh
{
int v;
double y;
};
//typedef 一个类型
typedef int my_int;
}
int main()
{
printf("%p\n", rand); //这里访问的是stdlib.h中的rand()函数的地址
printf("%d", fhvyxyci::rand); //::操作符用于访问命名空间中的成员,这里访问的是命名空间fhvyxyci中的rand变量
return 0;
}
namespace
只能定义在全局,当然它还可以嵌套定义。
#include<stdio.h>
#include<stdlib.h>
namespace fhvyxyci
{
namespace fhvy
{
int rand = 10;;
}
namespace xyci
{
int rand = 11;
}
}
int main()
{
printf("%d\n", fhvyxyci::fhvy::rand);
printf("%d\n", fhvyxyci::xyci::rand);
printf("%p\n", rand);
return 0;
}
namespace
会认为是一个namespace
,不会冲突。//test.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
namespace fhvyxyci
{
int rand = 11;
double fh = 10;
int add(int a, int b);
}
//test.cpp
#include"test.h"
namespace fhvyxyci
{
int add(int a, int b)
{
return a + b;
}
}
int main()
{
printf("%d\n", fhvyxyci::rand);
printf("%lf\n", fhvyxyci::fh);
printf("%d\n", fhvyxyci::add(1, 2));
return 0;
}
这个代码可以正常地运行,函数之间就如同正常的在头文件声明,.cpp
文件中定义一样,只不过都要放在名称相同的命名空间中。
std
(standard)的命名空间中。不过尽管C++兼容C,但C语言的库在.cpp
文件中并不在命名空间中,都存在于全局。编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以下面的情况1会编译报错。 我们要使用命名空间中定义的变量/函数,有三种方式:
using
将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。using
是C++的一个关键字,用于展开命名空间,使用方式请参考下面的4种情况。
当要在命名空间之外对命名空间中的成员进行任何操作,如访问,调用时,均使用::
操作符。
情况1:未做任何对应措施就尝试访问命名空间中的内容
#include<stdio.h>
namespace fhvyxyci
{
int a = 10;
int b = 11;
}
int main()
{
printf("%d\n", a);
return 0;
}
编译器给出报错:
情况2:指定命名空间访问
#include<stdio.h>
namespace fhvyxyci
{
int a = 10;
int b = 11;
}
int main()
{
printf("%d\n", fhvyxyci::a); // ::操作符用于访问命名空间中的成员
printf("%d\n", fhvyxyci::b);
return 0;
}
//该代码正常运行
情况3:using
将某个特定成员展开
#include<stdio.h>
namespace fhvyxyci
{
int a = 10;
int b = 11;
}
//展开命名空间fhvyxyci中的成员b,不需要指出类型
using fhvyxyci::b;
int main()
{
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
//该代码报错,与情况1的报错一样
情况4:using
将整个命名空间展开
#include<stdio.h>
namespace fhvyxyci
{
int a = 10;
int b = 11;
}
//展开整个命名空间fhvyxyci
using namespace fhvyxyci;
int main()
{
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
//该代码正常运行
谢谢你的阅读,喜欢的话来个点赞收藏评论关注吧! 我会持续更新更多优质文章