C++是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。C++目前被认为是一种中级语言,它综合了低级语言和高级语言的特点。C++ 是由 Bjarne Stroustrup 于 1979 年在新泽西州美利山贝尔实验室开始设计开发的。C++从C语言扩充发展而来,依次经历了new C -> C with class -> C++,在1983年更名为C++。因此,任何合法的C语言程序都是合法的C++程序。但是,C++语言检查器比C语言检查要严格匹配,C++严谨了编程风格。
当然这里也给一下其他各个语言的出现时期:B语言(1969)、C语言(1972)、C++语言(1983)Java语言(1996)与C#语言(2000)
标准的 C++ 由三个重要部分组成:
- 核心语言,提供了所有构件块,包括变量、数据类型和常量,等等。
- C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
- 标准模板库(STL),提供了大量的方法,用于操作数据结构等。
其中向对象程序设计包含四大特性:封装、抽象、继承、多态。
C++ 支持多种编程风格。您可以使用 Fortran、C、Smalltalk 等任意一种语言的编程风格来编写代码。每种风格都能有效地保证运行时间效率和空间效率。
C++基于现有的传承性,当前主要用来编写设备驱动程序和其他要求实时性的硬件操作软件,操作系统中很多常用接口都是直接使用C++编写的。当然C++也不是不可以用来编写界面程序,基于QT架构开发比较多见。
ANSI 标准是为了确保 C++ 的便携性 —— 您所编写的代码在 Mac、UNIX、Windows、Alpha 计算机上都能通过编译。由于 ANSI 标准已稳定使用了很长的时间,所有主要的 C++ 编译器的制造商都支持 ANSI 标准。表中给出了各个C++标准发布的详细日期。
发布时间 | 通称 | 备注 |
---|---|---|
2017 | C++17 | 第五个C++标准 |
2017 | coroutines TS | 协程库扩展 |
2017 | ranges TS | 提供范围机制 |
2017 | library fundamentals TS | 标准库扩展 |
2016 | concurrency TS | 用于并发计算的扩展 |
2015 | concepts TS | 概念库,用于优化编译期信息 |
2015 | TM TS | 事务性内存操作 |
2015 | parallelism TS | 用于并行计算的扩展 |
2015 | filesystem TS | 文件系统 |
2014 | C++14 | 第四个C++标准 |
2011 | - | 十进制浮点数扩展 |
2011 | C++11 | 第三个C++标准 |
2010 | - | 数学函数扩展 |
2007 | C++TR1 | C++技术报告:库扩展 |
2006 | - | C++性能技术报告 |
2003 | C++03 | 第二个C++标准 |
1998 | C++98 | 第一个C++标准 |
另外,对于gcc编译工具链所支持的对应的C++标准版本如列表下。对于不同类型的微软的VS版本可以自行查找。
GCC 版本 | C++常用标准 | |||||||
---|---|---|---|---|---|---|---|---|
C++98/03 | C++11 | C++14 | C++17 | GNU++98 | GNU++11 | GNU++14 | GNU++17 | |
10.1 ~ 8.4 | c++98/c++03 | c++11 | c++14 | c++17 | gnu++98/gnu++03 | gnu++11 | gnu++14 | gnu++17 |
7.5 ~ 5.5 | c++98/c++03 | c++11 | c++14 | c++1z(部分支持) | gnu++98/gnu++03 | gnu++11 | gnu++14 | gnu++1z(部分支持) |
4.9.4 ~ 4.8.5 | c++98/c++03 | c++11 | c++1y(部分支持) | gnu++98/gnu++03 | gnu++11 | gnu++1y(部分支持) | ||
4.7.4 | c++98 | c++11(部分支持) | gnu++98 | gnu++11(部分支持) | ||||
4.6.4 | c++98 | c++0x(部分支持) | gnu++98 | gnu++0x(部分支持) | ||||
4.5.4 | c++98 | c++0x(部分支持) | gnu++98 | gnu++0x(部分支持) |
向对象程序最关键的地方在于必须能够表现三大特性:封装,继承,多态!
封装指的是类中的敏感数据在外界是不能访问的;继承指的是可以对已经存在的类
进行代码复用,并使得类之间存在父子关系;多态指的是相同的调用语句可以产生
不同的调用结果。因此,如果希望用 C 语言完成面向对象的程序,那么肯定的,
必须实现这三个特性;否则,最多只算得上基于对象的程序(程序中能够看到对象
的影子,但是不完全具备面向对象的 3 大特性)。
课程中通过 void* 指针保证具体的结构体成员是不能在外界被访问的,以
此模拟 C++ 中 private 和 protected。因此,在头文件中定义了如下的语句:
typedef void Demo;
typedef void Derived;
Demo 和 Derived 的本质依旧是 void, 所以,用 Demo* 指针和 Derived* 指针
指向具体的对象时,无法访问对象中的成员变量,这样就达到了“外界无法访问类
中私有成员”的封装效果!
继承的本质是父类成员与子类成员的叠加,所以在用 C 语言写面向对象程
序的时候,可以直接考虑结构体成员的叠加即可。课程中的实现直接将 struct
ClassDemo d 作为 struct ClassDerived 的第一个成员,以此表现两个自定义数
据类型间的继承关系。因为 struct ClassDerived 变量的实际内存分布就是由
struct ClassDemo 的成员以及 struct ClassDerived 中新定义的成员组成的,这
样就直接实现了继承的本质,所以说 struct ClassDerived 继承自 struct
ClassDemo。
下一步要实现的就是多态了!多态在 C++ 中的实现本质是通过虚函数表完
成的,而虚函数表是编译器自主产生和维护的数据结构。因此,接下来要解决的问
题就是如何在 C 语言中自定义虚函数表?课程中认为通过结构体变量模拟 C++
中的虚函数表是比较理想的一种选择,所以有了下面的代码:
struct VTable
{
int (*pAdd)(void*, int);
};
必须要注意的是,课程中由于复制粘贴的缘故,误将 pAdd 指针的类型定义成了
int (*)(Derived*, int) , 这从 C 语言的角度算不上错误,因为 Derived* 的本质就
是 void* , 所以编译运行都没有问题。但是,从面向对象的角度,这里可以说是
一种语义上的错误!因为 pAdd 必须可以指向父类中定义的 Add 函数版本,也可
以指向子类中定义的 Add 函数版本,所以说用 Derived* 作为第一个参数表示实
际对象并不合适,应该直接使用 void* 。
有了类型后就可以定义实际的虚函数表了,在 C 语言中用具有文件作用域
的全局变量表示实际的虚函数表是最合适的,因此有了下面的代码:
// 父类对象使用的虚函数表
static struct VTable g_Demo_vtbl =
{
Demo_Virtual_Add
};
// 子类对象使用的虚函数表
static struct VTable g_Derived_vtbl =
{
Derived_Virtual_Add
};
每个对象中都拥有一个指向虚函数表的指针,而所有父类对象都指向
g_Demo_vtbl,所以所有子类对象都指向 g_Derived_vtbl。当一切就绪后,实际
调用虚函数的过程就是通过虚函数表中的对应指针来完成的。