编程语言排行
编程语言演变
编程语言图谱
Dennis M. Ritchie(丹尼斯·里奇),1941-2011,C语言之父、UNIX之父、黑客之父
Ken Thompson(肯·汤普逊),1943-,B语言之父、UNIX的发明人之一
Bjarne Stroustrup(本贾尼·斯特劳斯特卢普),1950-,C++之父
C++之父的个人主页:http://www2.research.att.com/~bs/homepage.html
Bjarne Stroustrup,1950年出生于丹麦,先后毕业于丹麦阿鲁斯大学和英国剑桥大学,AT&T大规模程序设计研究部门负责人,AT&T、贝尔实验室和ACM成员
最初导致C++诞生的原因是在Bjarne博士等人试图去分析UNIX内核的时候,这项工作开始于1979年4月,当时由于没有合适的工具能够有效地分析由于内核分布而造成的网络流量,以及怎样将内核模块化。同年10月,Bjarne博士完成了一个可以运行的预处理程序,称之为Cpre,它为C加上了类似Simula的类机制。在这个过程中,Bjarne博士开始思考是不是要开发一种新的语言,当时贝尔实验室对这个想法很感兴趣,就让Bjarne博士等人组成一个开发小组,专门进行研究
当时的C++还不叫C++,而是C with class,这是把它当作一种C语言的有效扩充。由于当时C语言在编程界居于老大的地位,要想发展一种新的语言,最强大的竞争对手就是C语言,所以当时有两个问题最受关注:C++要在运行时间、代码紧凑性和数据紧凑性方面能够与C语言相媲美,同时还要尽量避免在语言应用领域方面的限制。在这种情况下,一个很自然的想法就是让C++从C语言继承过来,但是Bjarne博士更具有先见之明,他为了避免受到C语言的局限,参考了很多语言,例如:从Simula继承了类的概念,从Algol68继承了运算符重载、引用以及在任何地方声明变量的能力,从BCPL获得了//注释,从Ada得到了模板、名字空间,从Ada、Clu和ML取来了异常
直到1998年,ANSI/ISO C++标准建立,同年,Bjarne博士推出了其经典著作《The C++ Programming Language》第三版。C++的标准化标志着Bjarne博士倾20年心血的伟大构想终于实现
1979:Bjarne博士在贝尔实验室完成了一个预处理程序——Cpre——为C加上了类似Simula的类机制
1983:第一个C with class实现投入使用,Rick Mascitti将其命名为C++
1985:CFront 1.0发布。Bjarne博士完成了经典巨著《The C++ Programming Language》第一版
1987:GNU C++发布
1990:Borland C++发布
1992:Microsoft C++发布。IBM C++发布
1998:ISO标准被批准,即C++98
2011:ISO发布ISO/IEC 14882:2011,即C++11
2014至今:ISO陆续发布C++14、C++17和C++20标准,C++23标准的正式版本即将发布
ISO/IEC 14882,正式发布于1998年,并于2003年更新,通常称作C++98/03。目前所有主流编译器均支持此标准
ISO/IEC 14882:2011,正式发布于2011年9月1日,官方名为Information technology - Programming languages - C++ Edition:3,即C++11,曾被临时命名为C++0x。目前绝大多数编译器都支持此标准
这是1998年以来C++语言的第一次大修订,对C++语言进行了改进和扩充,新的特性也扩展了语言在灵活性和效率上的传统优势
官网链接:http://www.iso.org/iso/pressrelease.htm?refid=Ref1472
Bjarne博士在自己网站上提供了该标准的草案文本,与最终的正式版本差别不大
C++14/17/20,在C++11的基础上,进一步扩充新的语法特性和标准库。目前只有部分编译器支持这些标准的部分特性
一方面在企业级系统(数据密集、业务规则复杂多变)开发中,C++已经基本被Java和C#等淘汰出局,另一方面在系统编程和嵌入式等更接近硬件的领域,又遭到C的强烈狙击
如果在你的应用中,有两个需求同时发生,你就必须要考虑采用C++,第一是对性能的要求高,第二是要有很强的抽象和建模能力
游戏:C++的效率是一个很重要的原因
科学计算:在科学计算领域,FORTRAN是使用最多的语言之一。但是近年来,C++凭借先进的数值计算库、泛型编程等优势在这一领域也应用颇多
网络和分布式应用:C++拥有很多成熟的用于网络通信的库,其中最具有代表性的是跨平台的、重量级的ACE库,该库可以说是C++语言最重要的成果之一,在许多重要的企业、部门甚至军方项目中都有应用
操作系统和设备驱动:在该领域,C语言是主要使用的编程语言。但是C++凭借其对C的兼容性和面向对象特性,也开始在该领域崭露头角
移动终端、嵌入式系统、教育科研、部分行业应用:C++、Java和C#这3种语言中,C++是最早出现的,保持了对C的兼容性,允许指针的存在,允许程序员手动高效地管理和使用内存(尽管这也是最容易引起问题的地方)
后缀++表达式的值,是变量自增前的值,这是否意味着广大C++程序员们,还在按照C的方式使用C++呢?
C语言
xxxxxxxxxx
831/* minisdk.cpp */
2
3
4
5
6HINSTANCE g_hInstance = NULL;
7TCHAR g_szWndTitle[] = _T("A Minimal SDK Program");
8TCHAR g_szWndClass[] = _T("MinSDK");
9
10LRESULT CALLBACK WndProc(HWND hWnd, UINT uMessage, WPARAM wParam, LPARAM lParam) {
11 switch (uMessage) {
12 case WM_DESTROY:
13 PostQuitMessage(0);
14 break;
15
16 default:
17 return DefWindowProc(hWnd, uMessage, wParam, lParam);
18 }
19
20 return 0;
21}
22
23ATOM InitApplication(HINSTANCE hInstance) {
24 WNDCLASSEX wcex;
25
26 wcex.cbSize = sizeof(WNDCLASSEX);
27 wcex.style = CS_HREDRAW | CS_VREDRAW;
28 wcex.lpfnWndProc = WndProc;
29 wcex.cbClsExtra = 0;
30 wcex.cbWndExtra = 0;
31 wcex.hInstance = hInstance;
32 wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
33 wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
34 wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
35 wcex.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
36 wcex.lpszMenuName = NULL;
37 wcex.lpszClassName = g_szWndClass;
38
39 return RegisterClassEx(&wcex);
40}
41
42BOOL InitInstance(HINSTANCE hInstance, int nCmdShow) {
43 HWND hWnd = CreateWindow(
44 g_szWndClass,
45 g_szWndTitle,
46 WS_OVERLAPPEDWINDOW,
47 CW_USEDEFAULT,
48 CW_USEDEFAULT,
49 CW_USEDEFAULT,
50 CW_USEDEFAULT,
51 NULL,
52 NULL,
53 hInstance,
54 NULL);
55
56 if (! hWnd)
57 return FALSE;
58
59 ShowWindow(hWnd, nCmdShow);
60 UpdateWindow(hWnd);
61
62 g_hInstance = hInstance;
63
64 return TRUE;
65}
66
67int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR pCmdLine, int nCmdShow) {
68 if (!hPrevInstance)
69 if (!InitApplication(hInstance))
70 return FALSE;
71
72 if (!InitInstance(hInstance, nCmdShow))
73 return FALSE;
74
75 MSG message;
76
77 while (GetMessage(&message, NULL, 0, 0)) {
78 TranslateMessage(&message);
79 DispatchMessage(&message);
80 }
81
82 return message.wParam;
83}
C++语言
xxxxxxxxxx
201// minimfc.cpp
2
3
4
5class CMinApp : public CWinApp {
6public:
7 BOOL InitInstance(void) {
8 CFrameWnd* pFrameWnd = new CFrameWnd;
9
10 pFrameWnd -> Create(0, _T("A Minimal MFC Program"));
11 pFrameWnd -> ShowWindow(SW_SHOWNORMAL);
12 pFrameWnd -> UpdateWindow();
13
14 m_pMainWnd = pFrameWnd;
15
16 return TRUE;
17 }
18};
19
20CMinApp MinApp;
C语言
xxxxxxxxxx
121/* ccheck.c */
2
3void func(unsigned char* puc) {
4}
5
6int main(void) {
7 char str[] = "hello";
8
9 func(str); /* 参数类型不匹配也能通过编译 */
10
11 return 0;
12}
C++语言
xxxxxxxxxx
121// cppcheck.cpp
2
3void func(unsigned char* puc) {
4}
5
6int main(void) {
7 char str[] = "hello";
8
9 func(str); // 参数类型不匹配无法通过编译
10
11 return 0;
12}
C语言
xxxxxxxxxx
241/* cenum.c */
2
3/* 在C语言中,枚举可以直接按整数处理 */
4
5
6
7typedef enum tag_LineStyle {
8 ELINESTYLE_SOLID,
9 ELINESTYLE_DASH,
10 ELINESTYLE_DOT,
11 ELINESTYLE_DASHDOT,
12 ELINESTYLE_DASHDOTDOT,
13 ELINESTYLE_NULL
14} ELINESTYLE;
15
16void drawLine(ELINESTYLE lineStyle, double startX, double startY, double endX, double endY) {
17 /* ... */
18}
19
20int main(void) {
21 drawLine(0, 0.0, 0.0, 100.0, 100.0); /* 以整型实参对应枚举型形参 */
22
23 return 0;
24}
C++语言
xxxxxxxxxx
241// cppenum.cpp
2
3// 在C++语言中,枚举是一个独立的数据类型,无法与整型互换
4
5
6
7typedef enum tag_LineStyle {
8 ELINESTYLE_SOLID,
9 ELINESTYLE_DASH,
10 ELINESTYLE_DOT,
11 ELINESTYLE_DASHDOT,
12 ELINESTYLE_DASHDOTDOT,
13 ELINESTYLE_NULL
14} ELINESTYLE;
15
16void drawLine(ELINESTYLE eLineStyle, double startX, double startY, double endX, double endY) {
17 // ...
18}
19
20int main(void) {
21 drawLine(0, 0.0, 0.0, 100.0, 100.0); // 将整型实参传给枚举形参,编译时错误,非法转换
22
23 return 0;
24}
最优匹配原则
xxxxxxxxxx
211// optimal.cpp
2
3// C++语言函数调用时的最优匹配原则
4
5
6
7using namespace std;
8
9void func(int n) {
10 cout << "func(int) invoked" << endl;
11}
12
13void func(char c) {
14 cout << "func(char) invoked" << endl;
15}
16
17int main(void) {
18 func('c'); // 匹配到func(char)版本
19
20 return 0;
21}
升级优先原则
xxxxxxxxxx
211// priupgrade.cpp
2
3// C++语言函数调用时的升级优先原则
4
5
6
7using namespace std;
8
9void func(int n) {
10 cout << "func(int) invoked" << endl;
11}
12
13void func(char c) {
14 cout << "func(char) invoked" << endl;
15}
16
17int main(void) {
18 func(true); // 匹配到func(int)版本
19
20 return 0;
21}
同名隐藏原则
xxxxxxxxxx
251// hide.cpp
2
3// C++语言函数调用时的同名隐藏原则
4
5
6
7using namespace std;
8
9void func(int n) {
10 cout << "func(int) invoked" << endl;
11}
12
13void func(char c) {
14 cout << "func(char) invoked" << endl;
15}
16
17int main(void) {
18 func(10); // 匹配到func(int)版本
19
20 void func(char c); // 后声明的标识符隐藏先声明的同名标识符
21
22 func(10); // 匹配到func(char)版本
23
24 return 0;
25}
等差歧义原则
xxxxxxxxxx
211// amb.cpp
2
3// C++语言函数调用时的等差歧义原则
4
5
6
7using namespace std;
8
9void func(int n) {
10 cout << "func(int) invoked" << endl;
11}
12
13void func(char c) {
14 cout << "func(char) invoked" << endl;
15}
16
17int main(void){
18 func(0.614); // 编译时错误,歧义
19
20 return 0;
21}
C语言
xxxxxxxxxx
171/* cimpdcl.c */
2
3/* C语言中返回值类型为int的函数可以不先声明而直接调用,谓之隐式声明 */
4
5
6
7int main(void) {
8 int n = func(); /* 返回值类型为int的函数可以未经声明而直接调用 */
9
10 printf("n=%d\n", n);
11
12 return 0;
13}
14
15int func(void) {
16 return 1000;
17}
C++语言
xxxxxxxxxx
191// cppimpdcl.cpp
2
3// C++语言不允许隐式声明
4
5
6
7using namespace std;
8
9int main(void) {
10 int n = func(); // 编译时错误,未声明
11
12 cout << "n=" << n << endl;
13
14 return 0;
15}
16
17int func(void) {
18 return 1000;
19}
通过名字空间可以对代码进行逻辑划分
xxxxxxxxxx
491// modules.cpp
2
3// 用名字空间对代码进行逻辑划分
4
5// 压缩解压缩模块
6namespace compress {
7 void zip(void) {
8 // ...
9 }
10
11 void unzip(void) {
12 // ...
13 }
14}
15
16// 加密解密模块
17namespace encrypt {
18 void des(void) {
19 // ...
20 }
21
22 void rsa(void) {
23 // ...
24 }
25}
26
27// 网络通信模块
28namespace network {
29 void send(void) {
30 // ...
31 }
32
33 void recv(void) {
34 // ...
35 }
36}
37
38int main(void) {
39 compress::zip();
40 compress::unzip();
41
42 encrypt::des();
43 encrypt::rsa();
44
45 network::send();
46 network::recv();
47
48 return 0;
49}
名字空间有助于避免名字冲突
xxxxxxxxxx
281// nameconf.cpp
2
3// 用名字空间防止名字冲突
4
5
6
7using namespace std;
8
9// 工行支付接口
10namespace icbc {
11 void pay(double money){
12 cout << "icbc::pay(" << money << ") invoked" << endl;
13 }
14}
15
16// 建行支付接口
17namespace ccb {
18 void pay(double money) {
19 cout << "ccb::pay(" << money << ") invoked" << endl;
20 }
21}
22
23int main(void) {
24 icbc::pay(100.00);
25 ccb::pay(200.00);
26
27 return 0;
28}
基本语法
xxxxxxxxxx
31namespace 名字空间名 {
2 名字空间成员
3}
在已有名字空间中添加成员
xxxxxxxxxx
291// append.cpp
2
3// 在已有名字空间中添加成员
4
5// 定义rip名字空间
6namespace rip {
7 void drawText(double x, double y, const char* text) {
8 // ...
9 }
10
11 void drawRect(double left, double top, double right, double bottom) {
12 // ...
13 }
14}
15
16// 在rip名字空间中添加drawCircle成员
17namespace rip {
18 void drawCircle(double x, double y, double radius) {
19 // ...
20 }
21}
22
23int main(void) {
24 rip::drawText(100.0, 200.0, "hello world");
25 rip::drawRect(300.0, 400.0, 500.0, 600.0);
26 rip::drawCircle(700.0, 800.0, 900.0);
27
28 return 0;
29}
名字空间的声明与定义可以分开
xxxxxxxxxx
731// divided.cpp
2
3// 名字空间的声明与定义可以分开
4
5
6
7using namespace std;
8
9namespace docbase {
10 extern unsigned int ndocs; // 在名字空间中声明变量
11
12 class Document { // 在名字空间中声明类
13 public:
14 Document(void);
15 ~Document(void);
16
17 void open(const char* path);
18 void close(void);
19 void save(void);
20 void saveAs(const char* path);
21 };
22
23 Document* createDocument(void); // 在名字空间中声明函数
24}
25
26// 定义名字空间中的变量、类和函数
27
28unsigned int docbase::ndocs = 0;
29
30docbase::Document::Document(void) {
31 // ...
32}
33
34docbase::Document::~Document(void) {
35 // ...
36}
37
38void docbase::Document::open(const char* path) {
39 // ...
40}
41
42void docbase::Document::close(void) {
43 // ...
44}
45
46void docbase::Document::save(void) {
47 // ...
48}
49
50void docbase::Document::saveAs(const char* path) {
51 // ...
52}
53
54docbase::Document* docbase::createDocument(void) {
55 Document* doc = new Document;
56
57 ndocs++;
58
59 return doc;
60}
61
62int main(void) {
63 cout << docbase::ndocs << endl;
64
65 docbase::Document* doc = docbase::createDocument();
66
67 cout << docbase::ndocs << endl;
68
69 doc->open("foo.doc");
70 doc->close();
71
72 return 0;
73}
作用域限定符
xxxxxxxxxx
191// scope.cpp
2
3// 在名字空间外部,必须通过作用域限定符(::),引用特定名字空间的成员
4
5
6
7using namespace std;
8
9namespace ns {
10 void foo(void) {
11 cout << "ns::foo() invoked" << endl;
12 }
13}
14
15int main(void) {
16 ns::foo(); // ns名字空间的foo
17
18 return 0;
19}
名字空间指令
xxxxxxxxxx
211// diruse.cpp
2
3// using指令表明在当前作用域中,可直接引用特定名字空间的成员
4
5
6
7using namespace std;
8
9namespace ns {
10 void foo(void) {
11 cout << "ns::foo() invoked" << endl;
12 }
13}
14
15int main(void) {
16 using namespace ns; // 在当前作用域中,可直接引用ns名字空间的成员
17
18 foo();
19
20 return 0;
21}
名字空间声明
xxxxxxxxxx
211// impname.cpp
2
3// using声明将特定名字空间的成员,引入当前作用域
4
5
6
7using namespace std;
8
9namespace ns {
10 void foo(void) {
11 cout << "ns::foo() invoked" << endl;
12 }
13}
14
15int main(void) {
16 using ns::foo; // 将ns名字空间的foo,引入当前作用域
17
18 foo();
19
20 return 0;
21}
同时使用名字空间指令和名字空间声明
xxxxxxxxxx
321// dclhide.cpp
2
3// 当前作用域中的名字,无论是直接声明的,还是通过using声明引入的,一定
4// 会隐藏其可见名字空间中的同名成员,即使using指令出现在using声明之后
5
6
7
8using namespace std;
9
10namespace ns1 {
11 void foo(void) {
12 cout << "ns1::foo() invoked" << endl;
13 }
14}
15
16namespace ns2 {
17 void foo(void) {
18 cout << "ns2::foo() invoked" << endl;
19 }
20}
21
22int main(void) {
23 using ns1::foo; // 将ns1名字空间的foo,引入当前作用域
24
25 foo();
26
27 using namespace ns2; // 在当前作用域中,可直接引用ns2名字空间的成员
28
29 foo(); // ns2名字空间的foo,被ns1名字空间的foo隐藏
30
31 return 0;
32}
无名名字空间
xxxxxxxxxx
311// noname.cpp
2
3// 无名名字空间的成员,可以直接引用,也可以通过全局作用域限定符(::)引用
4
5
6
7using namespace std;
8
9// 无名名字空间
10namespace {
11 void foo(void) {
12 cout << "::foo() invoked" << endl;
13 }
14}
15
16// 有名名字空间
17namespace ns {
18 void foo(void) {
19 cout << "ns::foo() invoked" << endl;
20
21 ::foo(); // 无名名字空间的foo
22 }
23}
24
25int main(void) {
26 ns::foo(); // ns名字空间的foo
27
28 foo(); // 无名名字空间的foo
29
30 return 0;
31}
xxxxxxxxxx
501// nestalias.cpp
2
3// 名字空间嵌套与名字空间别名
4
5
6
7using namespace std;
8
9namespace ns1 {
10 typedef int TYPE;
11
12 namespace ns2 {
13 typedef int* TYPE;
14
15 namespace ns3 {
16 typedef string TYPE;
17
18 namespace ns4 {
19 typedef string* TYPE; // 内层名字隐藏外层名字
20 }
21 }
22 }
23}
24
25int main(void) {
26 using namespace ns1;
27
28 TYPE n = 10; // int
29 cout << n << endl;
30
31 // 嵌套名字空间逐层分解
32
33 ns2::TYPE pn = &n; // int*
34 cout << *pn << endl;
35
36 ns2::ns3::TYPE str("hello"); // string
37 cout << str << endl;
38
39 ns2::ns3::ns4::TYPE pstr1 = &str; // string*
40 cout << *pstr1 << endl;
41
42 // 名字空间别名
43
44 namespace NS4 = ns2::ns3::ns4;
45
46 NS4::TYPE pstr2 = pstr1; // string*
47 cout << *pstr2 << endl;
48
49 return 0;
50}
C语言
xxxxxxxxxx
181/* stdio.c */
2
3/* C语言的格式化输出 */
4
5/* 头文件 */
6
7int main(void) {
8 char c = 'A';
9 int d = 100;
10 double g = 0.224;
11 char s[] = "hello";
12 int* p = &d;
13
14 /* printf()函数的格式标记必须与输出数据的类型相一致 */
15 printf("%c, %d, %g, %s, %p\n", c, d, g, s, p);
16
17 return 0;
18}
C++语言
xxxxxxxxxx
201// iostream.cpp
2
3// C++语言的格式化输出
4
5// 头文件
6
7using namespace std;
8
9int main(void) {
10 char c = 'A';
11 int d = 100;
12 double g = 0.224;
13 char s[] = "hello";
14 int* p = &d;
15
16 // cout为每种数据类型定义了相应的输出流插入操作符(<<),无需使用格式标记
17 cout << c << ", " << d << ", " << g << ", " << s << ", " << p << endl;
18
19 return 0;
20}
xxxxxxxxxx
161// bool.cpp
2
3// C++的bool类型
4
5
6
7using namespace std;
8
9int main(void) {
10 bool t = true, f = false;
11
12 cout << "Size of bool is " << sizeof(bool) << " byte(s)" << ", "
13 << "true is " << t << " and false is " << f << "." << endl;
14
15 return 0;
16}
引用即别名
xxxxxxxxxx
461// alias.cpp
2
3// 引用是另一个变量的别名
4
5
6
7using namespace std;
8
9int main(void) {
10 int n = 1;
11 cout << "n=" << n << endl;
12
13 // 通过指针修改变量的值
14 int* p = &n;
15 *p = 2;
16 cout << "n=" << n << endl;
17
18 // 通过引用修改变量的值
19 int& r = n;
20 r = 3;
21 cout << "n=" << n << endl;
22
23 // 通过引用同样可以实现变量的自增(减)
24 r++;
25 cout << "n=" << n << endl;
26
27 // 对引用取地址与对引用的目标取地址是一样的
28 cout << "&n=" << &n << ", &r=" << &r << endl;
29
30 // 不能声明指向引用的指针
31 // int&* pr = &r; // 编译错误
32
33 // 可以声明引用指针的引用
34 int*& rp = p;
35 (*rp)++;
36 cout << "n=" << n << endl;
37
38 // 引用数组的引用
39 int a[] = {1, 2, 3, 4, 5};
40 int (&ra)[5] = a;
41 for (int i = 0; i < sizeof (ra) / sizeof (ra[0]); i++)
42 cout << ra[i] << ' ';
43 cout << endl;
44
45 return 0;
46}
引用必须初始化
xxxxxxxxxx
241// init.cpp
2
3// 引用必须初始化
4
5
6
7using namespace std;
8
9const int c = 100;
10
11int main(void) {
12 int n;
13 int& r1 = n; // 引用一经定义就必须同时初始化
14
15 const int& r2 = 200; // 常量只能用于初始化常量引用
16 cout << "r2=" << r2 << endl;
17
18 // 试图通过常量类型转换修改常量的值可能导致不可预期的后果
19 const int& r3 = c;
20 const_cast<int&>(r3) = 300;
21 cout << "r3=" << r3 << ", c=" << c << endl;
22
23 return 0;
24}
引用必须与有效内存相关联
xxxxxxxxxx
211// valid.cpp
2
3// 引用必须与有效内存相关联
4
5
6
7using namespace std;
8
9int main(void) {
10 int* p1 = new int;
11 int& r1 = *p1;
12 r1 = 100;
13 cout << "r1=" << r1 << endl;
14
15 int* p2 = NULL;
16 int& r2 = *p2;
17 r2 = 200; // 运行错误
18 cout << "r2=" << r2 << endl;
19
20 return 0;
21}
引用型参数和引用型返回值
通过值、指针和引用传参
xxxxxxxxxx
371// vprparam.cpp
2
3// 通过值、指针和引用传参
4
5
6
7using namespace std;
8
9// 通过值传参
10void passVal(int arg) {
11 ++arg; // 形参是实参的副本
12}
13
14// 通过指针传参
15void passPtr(int* arg) {
16 ++*arg; // 形参是实参的地址
17}
18
19// 通过引用传参
20void passRef(int& arg) {
21 ++arg; // 形参是实参的别名
22}
23
24int main(void) {
25 int arg = 100;
26
27 passVal(arg); // 实参不变
28 cout << "passVal(): " << arg << endl;
29
30 passPtr(&arg); // 实参被函数改变
31 cout << "passPtr(): " << arg << endl;
32
33 passRef(arg); // 实参被函数改变
34 cout << "passRef(): " << arg << endl;
35
36 return 0;
37}
通过值、指针和引用返回
xxxxxxxxxx
401// vprret.cpp
2
3// 通过值、指针和引用返回
4
5
6
7using namespace std;
8
9int ret = 100;
10
11// 返回值
12int returnVal(void) {
13 return ret; // 返回变量的副本
14}
15
16// 返回指针
17int* returnPtr(void) {
18 return &ret; // 返回变量的地址
19}
20
21// 返回引用
22int& returnRef(void) {
23 return ret; // 返回变量的别名
24}
25
26int main(void) {
27 int val = returnVal(); // 得到的是副本
28 ++val;
29 cout << "returnVal(): " << ret << endl;
30
31 int* ptr = returnPtr(); // 得到的是地址
32 ++*ptr;
33 cout << "returnPtr(): " << ret << endl;
34
35 int& ref = returnRef(); // 得到的是别名
36 ++ref;
37 cout << "returnRef(): " << ret << endl;
38
39 return 0;
40}
值到值的函数调用和引用到引用的函数调用
xxxxxxxxxx
341// v2vr2r.cpp
2
3// 值到值的函数调用和引用到引用的函数调用
4
5
6
7using namespace std;
8
9// 通过值传递参数,通过值返回
10int v2v(int arg) {
11 cout << "v2v formal param: " << &arg << endl; // 打印形参的地址
12
13 return arg;
14}
15
16// 通过引用传递参数,通过引用返回
17int& r2r(int& arg) {
18 cout << "r2r formal param: " << &arg << endl; // 打印形参的地址
19
20 return arg;
21}
22
23int main (void) {
24 int arg = 100;
25 cout << " Actual param: " << &arg << endl; // 打印实参的地址
26
27 int val = v2v(arg);
28 cout << "v2v return value: " << &val << endl; // 打印返回值的地址
29
30 int& ref = r2r(arg);
31 cout << "r2r return value: " << &ref << endl; // 打印返回值的地址
32
33 return 0;
34}
作为函数返回值的引用,要保证在函数返回后其所引用的目标依然有效
xxxxxxxxxx
651// retvalid.cpp
2
3// 作为函数返回值的引用,要保证在函数返回后其所引用的目标依然有效
4
5
6
7using namespace std;
8
9string str("global");
10
11string& globalRef(void) {
12 return str; // 返回全局变量的引用,安全
13}
14
15string& heapRef(void) {
16 string* str = new string("heap");
17
18 return *str; // 返回堆变量的引用,安全
19}
20
21string& staticRef (void) {
22 static string str("static");
23
24 return str; // 返回静态变量的引用,安全
25}
26
27string& actualRef(string& str) {
28 return str; // 返回实参的引用,安全
29}
30
31class A {
32public:
33 A(void) : str("member") {}
34
35 string& memberRef(void) {
36 return str; // 返回成员变量的引用,安全
37 }
38
39private:
40 string str;
41};
42
43string& localRef(void) {
44 string str("local");
45
46 return str; // 返回局部变量的引用,危险
47}
48
49int main(void) {
50 cout << globalRef() << endl;
51
52 cout << heapRef() << endl;
53
54 cout << staticRef() << endl;
55
56 string str("actual");
57 cout << actualRef(str) << endl;
58
59 A a;
60 cout << a.memberRef() << endl;
61
62 // cout << localRef() << endl; // 运行错误
63
64 return 0;
65}
常引用型参数
接受常量实参
xxxxxxxxxx
211// crparam1.cpp
2
3// 常量引用可用于接受常量参数
4
5
6
7using namespace std;
8
9const double PI = 3.1415926;
10
11double circleArea(const double& radius) {
12 // 常量引用必须被常量初始化
13 return PI * radius * radius;
14}
15
16int main(void) {
17 // 常量只能用于初始化常量引用
18 cout << circleArea(12.34) << endl;
19
20 return 0;
21}
在保证传递效率的同时防止对实参的意外修改
xxxxxxxxxx
371// crparam2.cpp
2
3// 以常量引用作为参数可在保证传递效率的同时防止对实参的意外修改
4
5
6
7using namespace std;
8
9struct Student {
10 char name[256];
11 char gender[64];
12 int age;
13 char phone[256];
14 char email[256];
15 char address[1024];
16};
17
18void print(const Student& student) { // 以常量引用作为参数
19 // 无法修改形参所引用实参的内容
20 cout << student.name << ", " << student.gender << ", "
21 << student.age++ << ", " << student.phone << ", "
22 << student.email << ", " << student.address << endl; // 编译错误
23}
24
25int main(void) {
26 Student student = {
27 "张飞",
28 "男",
29 20,
30 "13910110072",
31 "zhangfei@tedu.cn",
32 "北京市海淀区学院8号写字楼D座D509达内职业教育" };
33
34 print(student);
35
36 return 0;
37}
引用与指针
指针可以不做初始化,其目标可在初始化后随意改变(除非是指针常量),而引用必须做初始化,且一旦初始化就无法改变其所引用的目标
xxxxxxxxxx
231// variableptr.cpp
2
3// 指针的目标可随意改变
4
5
6
7using namespace std;
8
9int main(void) {
10 int i = 1;
11 int* p = &i; // 初始化指针
12
13 int j = 2;
14 p = &j; // 改变指针的目标
15
16 cout << "i=" << i << ", j=" << j << endl;
17
18 *p = 3; // 对指针的新目标赋值
19
20 cout << "i=" << i << ", j=" << j << endl;
21
22 return 0;
23}
xxxxxxxxxx
231// fixedref.cpp
2
3// 引用一旦初始化就无法改变其所引用的目标
4
5
6
7using namespace std;
8
9int main(void) {
10 int i = 1;
11 int& r = i; // 初始化引用
12
13 int j = 2;
14 r = j; // 对引用的目标赋值,而非改变引用的目标
15
16 cout << "i=" << i << ", j=" << j << endl;
17
18 r = 3; // 引用的目标一旦确定即再无变化
19
20 cout << "i=" << i << ", j=" << j << endl;
21
22 return 0;
23}
可以定义指向指针的指针,但不能定义引用引用的引用
xxxxxxxxxx
31int a;
2int* p = &a;
3int** pp = &p; // ok
xxxxxxxxxx
31int a;
2int& r = a;
3int&& rr = r; // error
可以定义引用指针的引用,但不能定义指向引用的指针
xxxxxxxxxx
31int a;
2int* p = &a;
3int*& rp = p; // ok
xxxxxxxxxx
31int a;
2int& r = a;
3int&* pr = &r; // error
可以定义存储指针的数组,但不能定义存储引用的数组,但可以定义引用数组的引用
xxxxxxxxxx
21int a, b, c;
2int* parr[] = {&a, &b, &c}; // ok
xxxxxxxxxx
21int a, b, c;
2int& rarr[] = {a, b, c}; // error
xxxxxxxxxx
21int arr[] = {1, 2, 3};
2int (&rarr)[3] = arr; // ok
引用的本质是指针
xxxxxxxxxx
201// refptr.cpp
2//
3// 引用是通过指针实现的
4
5
6
7struct A {
8 char& r;
9};
10
11int main(void) {
12 char c;
13 printf("%p\n", &c);
14
15 A a = {c};
16 char** pp = (char**)&a;
17 printf("%p\n", *pp);
18
19 return 0;
20}
xxxxxxxxxx
471// generalconv.cpp
2
3// 通用类型转换:目标类型(源类型标识符)
4
5
6
7using namespace std;
8
9class A {
10public:
11 A(void) : m_a(100) {}
12
13 int m_a;
14};
15
16class B {
17public:
18 B(void) : m_b(200), m_str("B class") {}
19
20 void foo(void) {
21 cout << "B::foo() invoked" << endl;
22 }
23
24 int m_b;
25 string m_str;
26};
27
28int main(void) {
29 A a;
30
31 B* pb = (B*)(&a); // 将A*转换为B*
32
33 pb->foo(); // 函数属于类而非对象,正常
34
35 cout << pb->m_b << endl; // A中相同位置亦有整型成员,正常
36
37 // cout << pb->m_str << endl; // A中无此成员,崩溃
38
39 char* p = (char*)(pb); // 通用类型转换可在不同类型的指针间随意转换
40 long l = long(pb); // 通用类型转换可在指针和整型间随意转换,不收窄即可
41
42 // 通用类型转换不能在指针和除整型以外的其它基本类型间转换
43 // char c = char(pb);
44 // double f = double(pb);
45
46 return 0;
47}
xxxxxxxxxx
341// staticcast.cpp
2
3// 静态类型转换:static_cast<目标类型>(源类型标识符)
4
5class A {};
6class B : public A {};
7class C {};
8
9int main(void) {
10 A* pa;
11 B* pb;
12 C* pc;
13
14 // 两种不同的数据类型,只要在一个方向上能
15 // 做隐式转换,就能在相反方向上做静态转换
16 pa = pb;
17 // pb = pa;
18 pb = static_cast<B*>(pa);
19
20 // 两种不同的数据类型,如果在任意方向上都不能
21 // 做隐式转换,则在所有方向上也不能做静态转换
22 // pc = pb;
23 // pb = pc;
24 // pc = static_cast<C*>(pb);
25 // pb = static_cast<B*>(pc);
26
27 // 任意类型的指针都能隐式转换为void*,
28 // void*能静态转换为其它任意类型的指针
29 void* pv = pa;
30 // pa = pv;
31 pa = static_cast<A*>(pv);
32
33 return 0;
34}
xxxxxxxxxx
731// dynamiccast.cpp
2
3// 动态类型转换:dynamic_cast<目标类型>(源类型标识符)
4
5
6
7using namespace std;
8
9class A {
10public:
11 virtual void what(void) {
12 cout << "A class" << endl;
13 }
14};
15
16class B : public A {
17public:
18 void what(void) {
19 cout << "B class" << endl;
20 }
21};
22
23class C : public B {
24public:
25 void what(void) {
26 cout << "C class" << endl;
27 }
28};
29
30int main(void) {
31 B b;
32 b.what();
33
34 A* pa = &b; // 指向子类对象的基类指针
35 pa->what();
36
37 // 将基类指针动态类型转换为子类指针,若基类指针的目标
38 // 确为子类或子类之子类对象,则转换成功,否则返回NULL
39 B* pb = dynamic_cast<B*>(pa);
40 if (pb)
41 pb->what(); // 转换成功
42 else
43 cout << "Unable to cast A* to B*" << endl;
44
45 C* pc = dynamic_cast<C*>(pa);
46 if (pc)
47 pc->what();
48 else
49 cout << "Unable to cast A* to C*" << endl; // 转换失败
50
51 // 引用没有空值,故通过异常表示转换失败
52
53 A& ra = b;
54 ra.what();
55
56 try {
57 B& rb = dynamic_cast<B&>(ra);
58 rb.what(); // 转换成功
59 }
60 catch (bad_cast& ex) {
61 cout << "Unable to cast A& to B&: " << ex.what() << endl;
62 }
63
64 try {
65 C& rc = dynamic_cast<C&>(ra);
66 rc.what();
67 }
68 catch (bad_cast& ex) {
69 cout << "Unable to cast A& to C&: " << ex.what() << endl; // 转换失败
70 }
71
72 return 0;
73}
xxxxxxxxxx
411// constcast.cpp
2
3// 常量类型转换:const_cast<目标类型>(源类型标识符)
4
5
6
7using namespace std;
8
9int main(void) {
10 int n = 100;
11
12 int* p1 = &n;
13 ++*p1;
14 cout << n << endl;
15
16 // 从int*到const int*可以隐式转换
17 const int* p2 = p1;
18 // ++*p2;
19
20 // 从const int*到int*必须常量转换
21 // int* p3 = p2;
22 int* p3 = const_cast<int*>(p2);
23 ++*p3;
24 cout << n << endl;
25
26 int& r1 = n;
27 ++r1;
28 cout << n << endl;
29
30 // 从int&到const int&可以隐式转换
31 const int& r2 = r1;
32 // ++r2;
33
34 // 从const int&到int&必须常量转换
35 // int& r3 = r2;
36 int& r3 = const_cast<int&>(r2);
37 ++r3;
38 cout << n << endl;
39
40 return 0;
41}
常量类型转换只能改变标识符的const特性,比通用类型转换更安全
xxxxxxxxxx
301// constsafe.cpp
2
3// 常量类型转换只能去除标识符上的const限定,比通用类型转换更安全
4
5
6
7using namespace std;
8
9struct X {
10 int n[4096];
11 char c;
12};
13
14int main(void) {
15 char c = 'A';
16 cout << c << endl;
17
18 const char* p1 = &c;
19
20 char* p2 = const_cast<char*>(p1);
21 *p2 = 'B';
22 cout << c << endl;
23
24 // X* p3 = const_cast<X*>(p1); // 编译错误
25
26 X* p4 = (X*)p1;
27 p4->c = 'C'; // 运行错误
28
29 return 0;
30}
试图通过常量类型转换修改常量的值可能导致不可预期的后果
xxxxxxxxxx
191// constcannot.cpp
2
3// 试图通过常量类型转换修改常量的值可能导致不可预期的后果
4
5
6
7int main(void) {
8 // const int n = 123;
9 const volatile int n = 123;
10 printf("%p: %d\n", &n, n);
11
12 // n = 456;
13 int* p = const_cast<int*>(&n);
14 *p = 456;
15 printf("%p: %d\n", p, *p);
16 printf("%p: %d\n", &n, n);
17
18 return 0;
19}
xxxxxxxxxx
251// reinterpretcast.cpp
2
3// 重解释类型转换:reinterpret_cast<目标类型>(源类型标识符)
4
5
6
7int main(void) {
8 int n = 0x12345678;
9
10 // 重解释类型转换可在不同类型的指针间随意转换
11 char* p = reinterpret_cast<char*>(&n);
12 printf("%#08x: [%#02x|%#02x|%#02x|%#02x]\n", n, p[0], p[1], p[2], p[3]);
13
14 // 重解释类型转换可在指针和整型间随意转换,不收窄即可
15 long l = reinterpret_cast<long>(p);
16 printf("%#lx\n", l);
17 p = reinterpret_cast<char*>(l);
18 printf ("%p\n", p);
19
20 // 重解释类型转换不能在指针和除整型以外的其它类型间转换
21 // double d = reinterpret_cast<double>(p);
22 // p = reinterpret_cast<char*>(d);
23
24 return 0;
25}
xxxxxxxxxx
631// new.cpp
2
3// 在堆中分配和释放内存
4
5
6
7
8
9
10
11using namespace std;
12
13int main(void) {
14 int* p1 = (int*)malloc(sizeof(int)); // 用malloc函数分配堆内存
15 cout << *p1 << endl; // 堆内存默认初始化为0
16 free(p1); // 用free函数释放malloc函数分配的堆内存
17 // free(p1); // 不能释放已经释放过的堆内存
18 p1 = NULL; // 空指针比悬空指针安全
19 free(p1); // 释放空指针是空操作
20
21 int* p2 = new int; // 用new操作符分配堆内存
22 cout << *p2 << endl; // 堆内存默认初始化为0
23 delete p2; // 用delete操作符释放new操作符分配的堆内存
24 // delete p2; // 不能释放已经释放过的堆内存
25 p2 = NULL; // 空指针比悬空指针安全
26 delete p2; // 释放空指针是空操作
27
28 int* p3 = new int(100); // 在分配堆内存的同时,将其初始化为特定值
29 cout << *p3 << endl;
30 delete p3;
31
32 int* p4 = new int[5]; // 用new[]操作符在堆内存中分配数组
33 for (int i = 0; i < 5; ++i)
34 cout << p4[i] << ' '; // 堆内存中的数组默认初始化为0
35 cout << endl;
36 delete[] p4; // 用delete[]操作符释放堆内存中的数组
37
38 int* p5 = new int[5] {100, 200, 300};
39 // 在分配数组的同时,将各数组元素初始化为
40 // 特定值,未指定初始值的元素被初始化为0
41 for (int i = 0; i < 5; ++i)
42 cout << p5[i] << ' ';
43 cout << endl;
44 delete[] p5;
45
46 // 堆内存不够分配,malloc()函数会返回空指针,同时置errno
47 int* p6 = (int*)malloc(0xFFFFFFFFFFFFFF * sizeof(int));
48 if (p6)
49 free(p6);
50 else
51 cerr << strerror(errno) << endl;
52
53 // 堆内存不够分配,new操作符会抛出bad_alloc异常
54 try {
55 int* p7 = new int[0xFFFFFFFFFFFFFF];
56 delete[] p7;
57 }
58 catch (bad_alloc& ex) {
59 cerr << ex.what () << endl;
60 }
61
62 return 0;
63}
当使用new运算符定义一个多维数组变量或数组对象时,它产生一个指向数组第一个元素的指针,返回的类型保持了除最左边维数外的所有维数。例如:
xxxxxxxxxx
31int *p1 = new int[5];
2int (*p2)[5] = new int[4][5];
3int (*p3)[4][5] = new int[3][4][5];
内联函数保持了函数的特性,却避免了函数调用的开销
xxxxxxxxxx
141/* dangerousmacro.c */
2
3/* 宏的潜在风险 */
4
5
6
7
8
9int main(void) {
10 printf("%d\n", SQUARE(3)); /* 3*3 = 9 */
11 printf("%d\n", SQUARE(1+2)); /* 1+2*1+2 = 5 */
12
13 return 0;
14}
xxxxxxxxxx
181// safeinline.cpp
2
3// 内联函数保持了函数的特性,却避免了函数调用的开销
4
5
6
7using namespace std;
8
9inline int square(int x) {
10 return x * x;
11}
12
13int main(void) {
14 cout << square(3) << endl;
15 cout << square(1+2) << endl; // 内联函数也是函数,而非如宏般的文本替换
16
17 return 0;
18}
inline关键字仅仅表示希望该函数被编译为内联,到底会不会成为内联由编译器决定。通常情况下大函数和递归函数都不会被处理为内联函数
xxxxxxxxxx
231// cannotinline.cpp
2
3// inline关键字仅仅表示希望该函数被编译为内联,到底会不会成为内联
4// 由编译器决定。通常情况下大函数和递归函数都不会被处理为内联函数
5
6
7
8using namespace std;
9
10// 递归函数即使加了inline关键字,也不可能被编译器处理为内联函数
11inline long factorial(int n) {
12 if (n <= 1)
13 return 1;
14
15 return n * factorial(n - 1);
16}
17
18int main(void) {
19 for (int n = 0; n < 10; ++n)
20 cout << n << "! = " << factorial(n) << endl;
21
22 return 0;
23}
隐式内联和显示内联
隐式内联:在类声明中直接定义的函数自动被处理为内联函数
xxxxxxxxxx
241// impinline.h
2
3// 在类声明中直接定义的函数自动被处理为内联函数
4
5
6
7class Circle {
8public:
9 // 内联函数
10 Circle(double x, double y, double r) : x(x), y(y), r(r) {}
11
12 // 内联函数
13 double area(void) {
14 return 3.1415926 * square(r);
15 }
16
17private:
18 // 内联函数
19 double square(double d) {
20 return d * d;
21 }
22
23 double x, y, r;
24};
xxxxxxxxxx
171// impinline.cpp
2
3// 在类声明中直接定义的函数自动被处理为内联函数
4
5
6
7
8
9using namespace std;
10
11int main(void) {
12 Circle circle(0, 0, 3);
13
14 cout << circle.area() << endl;
15
16 return 0;
17}
显示内联:若不希望内联函数的定义出现在类声明中,则需在其声明处加inline关键字
xxxxxxxxxx
201// expinline.h
2
3// 若不希望内联函数的定义出现在类声明中,则需在其声明处加inline关键字
4
5
6
7class Circle {
8public:
9 // 非内联函数
10 Circle(double x, double y, double r);
11
12 // 非内联函数
13 double area(void);
14
15private:
16 // 内联函数
17 inline double square(double d);
18
19 double x, y, r;
20};
xxxxxxxxxx
321// expinline.cpp
2//
3// 若不希望内联函数的定义出现在类声明中,则需在其声明处加inline关键字
4
5
6
7
8
9using namespace std;
10
11// 非内联函数
12Circle::Circle(double x, double y, double r) : x(x), y(y), r(r) {}
13
14// 非内联函数
15double Circle::area(void)
16{
17 return 3.1415926 * square(r);
18}
19
20// 内联函数
21double Circle::square (double d)
22{
23 return d * d;
24}
25
26int main(void) {
27 Circle circle(0, 0, 3);
28
29 cout << circle.area() << endl;
30
31 return 0;
32}
同一作用域,函数名相同,参数表不同的函数构成重载关系
xxxxxxxxxx
381// overload.cpp
2
3// 重载函数:同一作用域,函数名相同,参数表不同
4
5
6
7using namespace std;
8
9void foo(int n, double d, const char* p) {
10 cout << "foo(int,double,const char*) invoked" << endl;
11}
12
13// 参数的总个数不同
14void foo(int n, double d) {
15 cout << "foo(int,double) invoked" << endl;
16}
17
18// 对应参数的类型不同
19void foo(double d, int n, const char* p) {
20 cout << "foo(double,int,const char*) invoked" << endl;
21}
22
23// 仅返回值不同,不能形成重载
24// char foo (int n, double d, const char* p) {
25// return 'A';
26// }
27
28int main(void) {
29 int n;
30 double d;
31 const char* p;
32
33 foo(n, d, p);
34 foo(n, d);
35 foo(d, n, p);
36
37 return 0;
38}
重载解析:完全匹配>常量转换>升级转换>标准转换>自定义转换>省略号匹配
xxxxxxxxxx
251// constupgrade.cpp
2
3// 重载解析,常量转换优先于升级转换
4
5
6
7using namespace std;
8
9// 需要升级转换(char->int)
10void foo(char* p, int n) {
11 cout << "foo(char*,int) invoked" << endl;
12}
13
14// 仅需常量转换(char*->const char*),优先
15void foo(const char* p, char c) {
16 cout << "foo(const char*,char) invoked" << endl;
17}
18
19int main(void) {
20 char *p, c;
21
22 foo(p, c);
23
24 return 0;
25}
xxxxxxxxxx
301// upgradestd.cpp
2
3// 重载解析,升级转换优先于标准转换
4
5
6
7using namespace std;
8
9// 需要标准转换(short->char)
10void foo(char c) {
11 cout << "foo(char) invoked" << endl;
12}
13
14// 仅需升级转换(short->int),优先
15void foo(int n) {
16 cout << "foo(int) invoked" << endl;
17}
18
19// 过分的升级转换(short->long long)
20void foo(long long l) {
21 cout << "foo(long,long) invoked" << endl;
22}
23
24int main(void) {
25 short s;
26
27 foo(s);
28
29 return 0;
30}
xxxxxxxxxx
261// ellipsis.cpp
2//
3// 重载解析,借助省略号所获得的匹配是最差匹配
4
5
6
7using namespace std;
8
9// 省略号匹配是最差匹配
10void foo(double d, ...) {
11 cout << "foo(double,...) invoked" << endl;
12}
13
14// 仅需标准转换(double->int),优先
15void foo(int n, void* p) {
16 cout << "foo(int,void*) invoked" << endl;
17}
18
19int main(void) {
20 double d;
21 void* p;
22
23 foo(d, p);
24
25 return 0;
26}
只有同一作用域中的同名函数才涉及重载问题,不同作用域中同名函数遵循标识符隐藏原则
xxxxxxxxxx
301// onescope.cpp
2
3// 只有同一作用域中的同名函数才涉及重载问题,不同作用域中的同名函数遵循标识符隐藏原则
4
5
6
7using namespace std;
8
9void foo(int n) {
10 cout << "foo(int=" << n << ") invoked" << endl;
11}
12
13void foo(double d) {
14 cout << "foo(double=" << d << ") invoked" << endl;
15}
16
17int main(void) {
18 foo(10); // 通过重载解析,匹配到全局域的foo(int)
19 foo(12.3); // 通过重载解析,匹配到全局域的foo(double)
20
21 void foo(int); // 在局部域中声明foo(int),隐藏全局域的foo(int)和foo(double)
22
23 foo(12.3); // 直接调用局部域的foo(int),局部域中无重载
24
25 void foo(double); // 在局部域中声明foo(double),与先前声明于该域的foo(int)构成重载
26
27 foo(12.3); // 通过重载解析,匹配到局部域的foo(double)
28
29 return 0;
30}
xxxxxxxxxx
371// twoscope.cpp
2
3// 只有同一作用域中的同名函数才涉及重载问题,不同作用域中的同名函数遵循标识符隐藏原则
4
5
6
7using namespace std;
8
9namespace ns1 {
10 void foo(int n) {
11 cout << "ns1::foo(int=" << n << ") invoked" << endl;
12 }
13}
14
15namespace ns2 {
16 void foo(double d) {
17 cout << "ns2::foo(double=" << d << ") invoked" << endl;
18 }
19}
20
21int main(void) {
22 using namespace ns1;
23 using namespace ns2;
24
25 foo(10); // 通过重载解析,匹配到全局域的ns1::foo(int)
26 foo(12.3); // 通过重载解析,匹配到全局域的ns2::foo(double)
27
28 using ns1::foo; // 将ns1::foo(int)引入局部域,隐藏全局域的ns1::foo(int)和ns2::foo(double)
29
30 foo(12.3); // 直接调用局部域的ns1::foo(int),局部域中无重载
31
32 using ns2::foo; // 将ns2::foo(double)引入局部域,与先前引至该域的ns1::foo(int)构成重载
33
34 foo(12.3); // 通过重载解析,匹配到局部域的ns2::foo(double)
35
36 return 0;
37}
函数指针的类型决定其具体指向哪个重载版本
xxxxxxxxxx
251// funcptr.cpp
2
3// 函数指针的类型决定其具体指向哪个重载版本
4
5
6
7using namespace std;
8
9void foo(int n) {
10 cout << "foo(int) invoked" << endl;
11}
12
13void foo(double d) {
14 cout << "foo(double) invoked" << endl;
15}
16
17int main(void) {
18 void (*funcptr1)(int) = foo; // ->foo(int)
19 void (*funcptr2)(double) = foo; // ->foo(double)
20
21 funcptr1(10);
22 funcptr2(12.3);
23
24 return 0;
25}
函数重载的本质是C++换名,即将函数参数表的类型信息编码到新的函数名中。以GNU C++编译器为例:
foo(int,double,const char*)
_Z3foodiPKc
foo(int,double)
_Z3fooid
foo(double,int,const char*)
_Z3fooidPKc
通过extern "C"指示C++编译器以C语言的方式处理函数接口,即不做换名,以方便C和C++代码的相互调用
C调C++
xxxxxxxxxx
131// cppdecl.h
2
3// 用C++语言编写的声明文件
4
5
6extern "C" {
7// __cplusplus
8
9void foo(int n, double d, const char* p);
10
11
12}
13// __cplusplus
xxxxxxxxxx
131// cppimpl.cpp
2
3// 用C++语言编写的实现文件
4
5
6
7
8
9using namespace std;
10
11void foo(int n, double d, const char* p) {
12 cout << "foo(int,double,const char*) invoked" << endl;
13}
xxxxxxxxxx
51g++ -c cppimpl.cpp
2nm cppimpl.o
3...
40000000000000000 T foo
5...
xxxxxxxxxx
151/* ccall.c */
2
3/* 用C语言编写的调用文件 */
4
5
6
7int main(void) {
8 int n;
9 double d;
10 const char* p;
11
12 foo(n, d, p);
13
14 return 0;
15}
xxxxxxxxxx
11gcc ccall.c cppimpl.o -lstdc++
C++调C
xxxxxxxxxx
51/* cdecl.h */
2
3/* 用C语言编写的声明文件 */
4
5void foo(int n, double d, const char* p);
xxxxxxxxxx
111/* cimpl.c */
2
3/* 用C语言编写的实现文件 */
4
5
6
7
8
9void foo(int n, double d, const char* p) {
10 printf("foo(int,double,const char*) invoked\n");
11}
xxxxxxxxxx
51gcc -c cimpl.c
2nm cimpl.o
3...
40000000000000000 T foo
5...
xxxxxxxxxx
191// cppcall.cpp
2
3// 用C++语言编写的调用文件
4
5extern "C" {
6
7
8
9}
10
11int main(void) {
12 int n;
13 double d;
14 const char* p;
15
16 foo(n, d, p);
17
18 return 0;
19}
xxxxxxxxxx
11g++ cppcall.cpp cimpl.o
既然extern "C"已经指示C++编译器以C语言的方式处理函数接口,即不做换名,这样的函数当然也就不可能再拥有重载版本
xxxxxxxxxx
151extern "C" {
2
3void foo(int n, double d, const char* p) {
4 cout << "foo(int,double,const char*) invoked" << endl;
5}
6/*
7void foo(int n, double d) {
8 cout << "foo(int,double) invoked" << endl;
9}
10
11void foo(double d, int n, const char* p) {
12 cout << "foo(double,int,const char*) invoked" << endl;
13}
14*/
15}
函数的参数可以取缺省值
xxxxxxxxxx
181// defparam.cpp
2
3// 函数的缺省(默认)参数
4
5
6
7using namespace std;
8
9void foo(int x, int y = 4) {
10 cout << "x=" << x << ", y=" << y << endl;
11}
12
13int main(void) {
14 foo(1, 2);
15 foo(3); // 未指定与形参y相对应的实参,故y取缺省值4
16
17 return 0;
18}
函数的缺省参数只能出现在函数声明中,且必须位于参数表的右端
xxxxxxxxxx
371// defright.cpp
2
3// 函数的缺省参数只能出现在函数声明中,且必须位于参数表的右端
4
5
6
7using namespace std;
8
9void foo(int x, int y, int z = 3);
10void bar(int x, int y = 5, int z = 6);
11void hum(int x = 7, int y = 8, int z = 9);
12// void err(int x, int y = 6, int z); // 编译错误
13
14int main(void) {
15 foo(1, 2);
16 bar(4);
17 hum();
18 // err(3, 9);
19
20 return 0;
21}
22
23void foo(int x, int y, int z /* = 3 */) {
24 cout << "foo(" << x << ',' << y << ',' << z << ") invoked" << endl;
25}
26
27void bar(int x, int y /* = 5 */, int z /* = 6 */) {
28 cout << "bar(" << x << ',' << y << ',' << z << ") invoked" << endl;
29}
30
31void hum(int x /* = 7 */, int y /* = 8 */, int z /* = 9 */) {
32 cout << "hum(" << x << ',' << y << ',' << z << ") invoked" << endl;
33}
34
35// void err(int x, int y /* = 6 */, int z) {
36// cout << "err(" << x << ',' << y << ',' << z << ") invoked" << endl;
37// }
缺省参数可能引发重载歧义
xxxxxxxxxx
221// defamb.cpp
2
3// 缺省参数可能引发重载歧义
4
5
6
7using namespace std;
8
9void foo(int x, int y = 4) {
10 cout << "x=" << x << ", y=" << y << endl;
11}
12
13void foo(int x) {
14 cout << "x=" << x << endl;
15}
16
17int main (int argc, char* argv[]) {
18 foo(1, 2);
19 foo(3); // 编译错误,既可以匹配到foo(int,int),y取缺省值,也可以匹配到foo(int),歧义
20
21 return 0;
22}
借助哑元参数保证函数调用的向后兼容
xxxxxxxxxx
241// forbkwd.cpp
2
3// 借助哑元参数保证函数调用的向后兼容
4/*
5// Ver. 1.0
6void func (bool b) {
7 if (b) {
8 // ...
9 }
10 else {
11 // ...
12 }
13}
14*/
15// Ver. 2.0
16void func (bool) { // 升级后的版本已经不再需要参数
17 // ...
18}
19
20int main (int argc, char* argv[]) {
21 func (true); // 哑元参数使函数调用语句无需修改
22
23 return 0;
24}
借助哑元参数构造函数的重载版本
xxxxxxxxxx
221// forover.cpp
2
3// 借助哑元参数构造函数的重载版本
4
5
6
7using namespace std;
8
9void foo(int n) {
10 cout << "foo(int) invoked" << endl;
11}
12
13void foo(int n, int) { // 哑元参数仅为与foo(int)构成重载
14 cout << "foo(int,int) invoked" << endl;
15}
16
17int main(void) {
18 foo(1); // 匹配到foo(int)
19 foo(2, 3); // 匹配到foo(int,int)
20
21 return 0;
22}
C语言
xxxxxxxxxx
381/* cdeclare.c */
2
3/* 传统意义上的C语言要求作用域内的任何声明都必须位于该作用域的开始处 */
4
5
6
7size_t getFilesize(const char* filepath) {
8 size_t filesize;
9
10 /* ... */
11
12 return filesize;
13}
14
15int main(void) {
16 /* 对局部变量的声明必须位于函数中任何可执行语句之前 */
17 size_t filesize;
18 unsigned char* filedata;
19 int i;
20
21 /* ... */
22
23 filesize = getFilesize("foo.dat");
24
25 if (filesize > 0) {
26 filedata = (unsigned char*)malloc(filesize);
27
28 /* ... */
29
30 free(filedata);
31 }
32
33 for (i = 0; i < 10; ++i) {
34 /* ... */
35 }
36
37 return 0;
38}
C++语言
xxxxxxxxxx
351// cppdeclare.cpp
2
3// C++语言允许数据声明与函数可执行代码相混合
4
5
6
7size_t getFilesize(const char* filepath) {
8 size_t filesize;
9
10 // ...
11
12 return filesize;
13}
14
15int main(void) {
16 // ...
17
18 // 变量的声明、定义和初始化在同一条语句中完成
19 size_t filesize = getFilesize("foo.dat");
20
21 if (filesize > 0) {
22 // 在语句块内部定义只在该块中使用的变量
23 unsigned char* filedata = new unsigned char[filesize];
24
25 // ...
26
27 delete[] filedata; // 用new/delete取代malloc/free
28 }
29
30 for (int i = 0; i < 0; ++i) { // 在循环语句内部定义循环控制变量
31 // ...
32 }
33
34 return 0;
35}
C语言
xxxxxxxxxx
341/* cmacro.c */
2
3/* C语言程序中宏和条件编译的使用 */
4
5
6
7// 常量宏
8
9// 条件编译宏
10 // 唯一标识宏
11
12
13
14
15
16
17
18
19
20
21// 参数宏
22
23int main(void) {
24 printf("PI=%g\n", PI);
25
26 printf("SHAPE_RECT=%d\n", SHAPE_RECT);
27 printf("SHAPE_ROUNDRECT=%d\n", SHAPE_ROUNDRECT);
28 printf("SHAPE_CIRCLE=%d\n", SHAPE_CIRCLE);
29 printf("SHAPE_ELLIPSE=%d\n", SHAPE_ELLIPSE);
30
31 printf("max(21,37)=%d\n", max(21, 37));
32
33 return 0;
34}
C++语言
xxxxxxxxxx
471// cppconst.cpp
2
3// C++语言程序用const取代常量宏,用enum取代唯一标识宏,用
4// inline取代参数宏,用namespace取代条件编译宏解决名字冲突
5
6
7
8using namespace std;
9
10const double PI = 3.1415926; // 用const取代常量宏
11
12namespace tarena_gdi { // 用namespace取代条件编译宏解决名字冲突
13 enum Shape { // 用enum取代唯一标识宏
14 SHAPE_RECT,
15 SHAPE_ROUNDRECT,
16 SHAPE_CIRCLE,
17 SHAPE_ELLIPSE
18 };
19}
20
21namespace mozila_gdi {
22 enum Shape {
23 SHAPE_RECT = 10,
24 SHAPE_ROUNDRECT,
25 SHAPE_CIRCLE,
26 SHAPE_ELLIPSE
27 };
28}
29
30inline int max(int a, int b) { // 用inline取代参数宏
31 return a > b ? a : b;
32}
33
34int main(void) {
35 using namespace tarena_gdi;
36
37 cout << "PI=" << PI << endl;
38
39 cout << "SHAPE_RECT=" << SHAPE_RECT << endl;
40 cout << "SHAPE_ROUNDRECT=" << SHAPE_ROUNDRECT << endl;
41 cout << "SHAPE_CIRCLE=" << SHAPE_CIRCLE << endl;
42 cout << "SHAPE_ELLIPSE=" << SHAPE_ELLIPSE << endl;
43
44 cout << "max(21,37)=" << max(21, 37) << endl;
45
46 return 0;
47}
C语言
xxxxxxxxxx
261/* cstring.c */
2
3/* 复杂而危险的C风格字符串 */
4
5
6
7
8
9int main(void) {
10 /* 缺乏独立的字符串类型,用字符数组表示字符串 */
11 char firstName[] = "Bjarne";
12 char familyName[] = "Stroustup";
13
14 char* fullName = malloc(strlen(firstName) + strlen(familyName) + 2);
15 /* 缓冲区分配不足极易导致溢出 */
16
17 strcpy(fullName, firstName); /* 危险的strcpy */
18 strcat(fullName, " "); /* 危险的strcat */
19 strcat(fullName, familyName); /* 危险的strcat */
20
21 printf("%s\n", fullName); /* 需要正确使用格式化占位符 */
22
23 free(fullName); /* 自己维护缓冲区的分配与释放 */
24
25 return 0;
26}
C++语言
xxxxxxxxxx
211// cppstring.cpp
2
3// 简洁而安全的C++字符串
4
5
6
7using namespace std;
8
9int main(void) {
10 // std提供了独立的字符串类型,自行维护缓冲区,对程序员透明
11 string firstName = "Bjarne";
12 string familyName = "Stroustup";
13
14 // 重载加号运算符,代码更简洁,缓冲区自动扩展,使用更安全
15 string fullName = firstName + " " + familyName;
16
17 // 输入输出流直接支持字符串类型
18 cout << fullName << endl;
19
20 return 0;
21}
C语言
xxxxxxxxxx
231/* cfunc.c */
2
3/* 面向过程的C语言程序,用函数处理数据 */
4
5/* 绘制矩形的函数 */
6void drawRect(double l, double t, double r, double b) {
7 /* ... */
8}
9
10/* 绘制圆形的函数 */
11void drawCircle(double x, double y, double r) {
12 /* ... */
13}
14
15int main(void) {
16 /* 绘制矩形 */
17 drawRect(100, 200, 300, 400);
18
19 /* 绘制圆形 */
20 drawCircle(500, 600, 700);
21
22 return 0;
23}
C++语言
x1// cppobj.cpp
2
3// 面向对象的C++程序,用类描述世界
4
5// 矩形类
6class Rect {
7public:
8 Rect(double l, double t, double r, double b) : l(l), t(t), r(r), b(b) {}
9
10 // 绘制方法
11 void draw(void) {
12 // ...
13 }
14
15private:
16 // 属性
17 double l, t, r, b;
18};
19
20// 圆形类
21class Circle
22{
23public:
24 Circle(double x, double y, double r) : x(x), y(y), r(r) {}
25
26 // 绘制方法
27 void draw(void) {
28 // ...
29 }
30
31private:
32 // 属性
33 double x, y, r;
34};
35
36int main(void) {
37 // 创建矩形对象
38 Rect rect(100, 200, 300, 400);
39 // 矩形绘制自己
40 rect.draw ();
41
42 // 创建圆形对象
43 Circle circle(500, 600, 700);
44 // 圆形绘制自己
45 circle.draw ();
46
47 return 0;
48}