> 文章列表 > 从C语言到C++(第一章)C++介绍和编写hello world!

从C语言到C++(第一章)C++介绍和编写hello world!

从C语言到C++(第一章)C++介绍和编写hello world!

目录

前言

1.认识C++ 

 2.C++的重要性

3.如何学习C++

4.编写hello world!

5.关键字

6.命名空间(namespace)

6.1命名空间的定义

6.2命名空间里的内容

 6.3命名空间重名问题

 6.4命名空间展开问题

6.5匿名命名空间

7.C++的输入与输出

7.1 输入和输出 

7.1.1 流插入运算符 << 

7.1.2 流提取运算符 >>

7.2 C和C++的混合使用

7.3 再看命名空间的展开

8.再看hello world!


前言

拖了这么久终于开始更传说中的C++了,虽然可能听说C++是掌控一切的,很难,但是认真学还是能学好的。未来改变世界的能力就交给各位了。

本系列文章默认已经学过C语言,并且对数据结构的一小部分知识有简单了解,(只要看过前面的专栏绝对够了)建议在正式开启学习C++之前,可以复习一下C语言相关的知识。

①零基础C语言程序设计科班作业_GR C的博客-CSDN博客

②C语言知识点和作业_GR C的博客-CSDN博客

③数据结构与算法(初阶)C语言描述_GR C的博客-CSDN博客

1.认识C++ 

C语言是结构化、模块化的语言,适合处理较小规模的程序。对于复杂和规模较大的程序,需要高度的抽象和建模时,C语言就太不合适了。C语言是面向过程的程序设计语言。

为了解决软件危机,20世纪80年代,计算机提出了 OOP(Object Oriented Programming),即面向对象思想,支持面向对象的程序设计语言C++就应运而生了。C++是由C语言发展而来的,所以C++兼容C语言,C++ 在西方称为C Puls Puls(CPP)

下图是C++之父---本贾尼

1979年,贝尔实验室的本贾尼等人在试图分析 Unix 内核的时候,试图将内核模块化,于是在C语言的基础上进行扩展,增加了类的机制,完成了一个可以运行的预处理程序,称之为 C with classes!后面在C++的发展中发展到了很多的阶段。 

在1998年定的标注,这个版本之后的 C++ 就相对比较完善了。支持了泛型编程,引入了STL标准模板库。 

 之后C++的更新其实是有点慢的, 比如 C++03 和 C++05 主要是修订一些东西。直到 C++11 才开始新增了新的东西,在这之后 14 17 也是在修订……如今出了 C++20,也是一个备受吐槽的版本,因为没有加啥特性。所以我们主要讲的是C++98和C++11。

 2.C++的重要性

C++ 到现在为止已经将近有40年了,目前最主流的两门语言 C/C++ 和 Java,其次就是 Python了,近几年机器学习的算法,上层是用 Python 写的,再其次就是 Go,也是蛮火的。

C# 当年是微软搞出来为了对抗 Java 的语言,C#最大的限制就是他是 Windows 上的,发展路径可能不好。
从语言发展的角度来说,C++是主流语言。再说,学完C++上手其他语言也是非常快的。服务端开发、大型软件、游戏开发……其中游戏开发里很多用的也都是C++。

3.如何学习C++

多敲代码,多总结!记笔记、画思维导图

常看书:

入门推荐看:高质量程序设计指南————C++/C语言

进阶推荐看: EffectiveC++   C++Primer

别人是怎么学的:

以下引用自2010年8月号《程序员》刊登的拙文《C++强大背后》最后一段:

C++缺点之一,是相对许多语言复杂,而且难学难精。

许多人说学习C语言只需一本K&R《C程序设计语言》 即可,但C++书籍却是多不胜数。我是从C进入C++,皆是靠阅读自学。在此分享一点学习心得。个人认为, 学习C++可分为4个层次:

第一个层次,C++基础  (平平常常)

挑选一本入门书籍,如《C++ Primer》、《C++大学教程》或Stroustrup撰写的经典《C++程序设计语 言》或他一年半前的新作《C++程序设计原理与实践》,而一般C++课程也止于此,另外《C++ 标准程 序库》及《The C++ Standard Library Extensions》可供参考;

第二个层次,正确高效的使用C++ (驾轻就熟)

此层次开始必须自修,阅读过《(More)Effective C++》、《(More)Exceptional C++》、 《Effective STL》及《C++编程规范》等,才适宜踏入专业C++开发之路;

第三个层次,深入解读C++ (出神入化)

关于全局问题可读《深入探索C++对象模型》、《Imperfect C++》、《C++沉思录》、《STL源码剖 析》,要挑战智商,可看关于模版及模版元编程的书籍如《C++ Templates》、《C++设计新思维》、 《C++模版元编程》;

第四个层次,研究C++  (返璞归真)

阅读《C++语言的设计和演化》、《编程的本质》(含STL设计背后的数学根基)、C++标准文件 《ISO/IEC 14882:2003》、C++标准委员会的提案书和报告书、关于C++的学术文献。

建议不要把「精通C++」作为一个一年目标,应该要把学习语言作为一个持续的过程,同时要把语言运用在具体的应用场合中。 

4.编写hello world!

新生婴儿会以自己独特的方式向这个崭新的世界打招呼,C++刚出来后,也算是一个新事物,

C++是否也应该向这个美好的世界来声问候呢?我们来看下C++是如何来实现问候的。

打开编译器创建完新项目后,右键源文件,点击 "添加新建项" 。

以前我们取得是Test.c现在名称我们就取为 Test.cpp,这样我们的代码就能很好地创建出来了:

创建完毕后,我们就可以开始打代码了:

#include <iostream>
using namespace std;int main()
{cout << "hell world!" << endl;return 0;
}

Hello World! 就能成功打印出来了

 以上iostream是c++输入输出流的头文件,和C语言的stdio.h类似

using namespace std;是展开std的命名空间

cout 与printf 类似 ,<< 是流输出运算符, endl是换行

和刚学C语言一样,写C++是我们也应该先把框架敲出来,下面我们详细介绍框架内容

5.关键字

C++ 标准共计有 63 个关键字 (C++11),其中包括 C 语言的 32 个关键字。

我们不会对关键字进行具体的讲解,后续都慢慢会学到的

目前我们只需要注意一点:命名时要避开这些关键字!

如果把变量命名为关键字,编译器将无法识别这个标识符!从而导致 编译错误 !

注意下面这些关键字,对于刚学完 C 的初学者来说有很多 "生词" ,我们先混个眼熟:

 不要背,这里把关键字表列出来,是为了能让大家在初学 C++ 时,少踩命名的坑!

6.命名空间(namespace)

在 C/C++ 中,变量、函数和类都是大量存在的,这些变量、函数和类的名称,

都会存在于全局作用域中,这么一来就会导致 命名冲突 问题……

C++ 引入了 namespace,只要合理使用,就可以有效地避免命名冲突问题!

使用命名空间的目的:对标识符的名称进行本地化,以避免命名冲突或名字污染。 

我们刚才 HelloWorld 代码中的 namespace 的出现,就是针对这个问题的。

比如在 stdlib 库中有一个生成随机数的函数 rand() ,

相信大家都认识,但是我们假装某个人不知道 stdlib 库中有一个叫 rand 的函数存在,

因此在定义变量时给变量取名为 rand 。

#include<stdio.h>
#include<stdlib.h>int rand = 525;int main()
{printf("%d\\n", rand);// 这里到底是打印我们自己定义的rand,还是stdlib里的?return 0;
}

我们知道,#include 包含头文件,头文件里的内容是会被展开来的。

当展开头文件时,stdlib 库中有一个叫 rand 的函数,我这里又定义了一个叫 rand 的变量,

此时就冲突了!冲突了,那么问题来了,我们这里 printf 打印出来的会是什么呢?

编译器的寻找规则: 局部找 → 全局找 → 找不到(报错)。

问题是在C语言里几乎没有办法很好地解决这种问题。

所以为了很好地解决这种冲突的问题,C++ 就加入了命名空间的特性!

在 C++ 里,我们就可以利用 "命名空间" 来解决这个问题,

所以 C++ 提出了一个新语法 —— 命名空间 namespace!

6.1命名空间的定义

定义命名空间,肯定得用到我们刚才提到的 namespace 关键字,

namespace 后面可以取一个空间名,然后再接上一对大括号就可以了

 我们该如何使用它呢?修改上面的代码:

#include<stdio.h>
#include<stdlib.h>namespace rtx//自己定义的命名空间可以自己命名
{int rand = 525;
}int main()
{printf("%d\\n", rtx::rand);//这样就能打印我们定义的rand了return 0;
}

这里的 : :  叫做 "作用域限定符" 。这么一来,就不怕冲突了,问题就这么轻松地解决了。

命名空间能够达到一种类似于 "隔离" 的效果。

注意事项:

①命名空间必须在全局作用域下定义!

其次,正是因为命名空间是全局的,所以这个 rand 变量也自然而然地变成了全局变量。

②命名空间长得有点像结构体,但是它和结构体不是一个东西,结构体是定义一个类型,

它们的性质是完全不一样的。还有,命名空间大括号外不用加分号。

6.2命名空间里的内容

命名空间里的内容,不仅仅可以存放变量,

还可以在里面放函数,结构体,甚至是 类(我们后面会讲)。

还可以在命名空间里放命名空间,(命名空间的嵌套,就是需要用多层:: 没什么用)

#include <stdio.h>
namespace N1 
{int a = 10;int b = 20;int Add(int x, int y) {return x + y;}
}
namespace N2 
{int c = 0;struct Node {struct Node* next;int val;};
}
int main()
{int res = N1::Add(N1::a, N1::b);printf("result = %d", res);struct N2::Node node1;return 0;
}

 6.3命名空间重名问题

命名空间是用来解决命名冲突问题的,

那我项目中定义的某个命名空间的名字和其他命名空间的名字冲突了怎么办?

其实, 在同一个工程中是允许存在多个相同名称的命名空间的。

编译器最后会将他们合成到同一个命名空间中的。

有两个相同名字的命名空间,就会合二为一。有三个相同名字的命名空间,就会三合一……

总结:同一个工程中允许存在多个相同的命名空间,编译器最后会将它们合成到一起。

 6.4命名空间展开问题

假设有这样的一种情况,一个命名空间中的某个成员是我们经常要使用的:

#include <stdio.h>
namespace N1 
{int a = 10;  // 假设a经常需要使用int b = 20;int c = 30;
}void func(int n) 
{printf("HI, %d\\n", n);
}int main()
{printf("%d\\n", N1::a);int res = N1::a;func(N1::a);printf("hello, %d\\n", N1::a);return 0;
}

指定的作用域,能够做到最好的命名隔离,但是使用起来好像不是很方便。

每次使用都要调 : :  这也太难受了,有办法能解决吗?

我们可以用 using namespace 将整个命名空间展开,因为命名空间是在全局土生土长的,所以展开后,里面的东西自然会被展开到全局

using namespace 空间名;
#include <stdio.h>namespace N1 
{int a = 10;  // 假设a经常需要使用int b = 20;int c = 30;
}void func(int n)
{printf("HI, %d\\n", n);
}using namespace N1;  // 将N1这个命名空间展开int main()
{printf("%d\\n", a); // 这样我们就可以直接使用了,就不需要 "::" 了int res = a;func(a);printf("hello, %d\\n", a);return 0;
}

6.5匿名命名空间

我们在C语言学习结构体的时候,我们就提到过匿名结构体。

命名空间这里也可以匿名!如果一个命名空间没有名称,我们就称它为匿名结构体。

// 匿名命名空间
namespace
{char c;int i;double d;
}

这种情况,编译器会在内部给这个没有名字的 "匿名命名空间" 生成一个惟一的名字。

并且还会为该匿名命名空间生成一条 using 指令,所以上面的代码会等同于:

namespace _UNIQUE_NAME 
{char c;int i;double d;
}
using namespace _UNIQUE_NAME;

     这里我们只需要知道有这么一个东西就可以了

7.C++的输入与输出

我们刚才之所以讲解命名空间,就是为了让大家能够慢慢地看懂前面写的 HelloWorld。

#include <iostream>
using namespace std;int main()
{cout << "hell world!" << endl;return 0;
}

但是在讲解C++的输入与输出之前,我们还需要再对命名空间做一个小小的补充。

库也是会用命名空间的,C++库的实现定义包含在了一个叫 std 的命名空间中。

我们加上 using namespace std ; 是为了把 std 空间中所有的内容都展开,

这样我们就可以直接使用它们了,前面说过这样实际是不好的,但平时练习可以这样写。

为什么C++要把它封装到一个叫 std 的命名空间中呢?

因为这样就不容易冲突了,有效放置了冲突命名。

注意事项:这里要提一下,有些老的教材上有  #include <iostream.h>

这个在老的编译器上是可以的,比如 VC6.0 ,比较老的版本的库,没有命名空间。

早期标准库将所有功能在全局域中实现,声明在.h后缀的头文件中,使用时只需包含对应头文件 即可,后来将其实现在std命名空间下,为了和C头文件区分,也为了正确使用命名空间,规定C++头文 件不带.h;是因为新的c++摈弃了.h形式的头文件。现在是标准c的头文件是.h,标准c++头文件不带.h。旧编译器(vc 6.0)中还支持 <iostream.h> 格式,后续编译器已不支持,因此推荐使用 <iostream> +std 的方式。

7.1 输入和输出 

cout 标准输出(控制台)和 cin 标准输入(键盘)时,必须包含 <iostream> 头文件。

并且需要使用 std 标准命名空间,这里我们下面会详细探讨。

7.1.1 流插入运算符 << 

cout 后面的 << ,我们称之为 "流插入运算符" 。就像水流的流向一样。

cout 用法演示:

#include <iostream>
using namespace std;int main()
{cout << "Hello";  // 等同于 printf("Hello");return 0;
}

上面的代码,我们之所以可以直接用 cout,是因为我们已经把 std 这块库命名空间展开了。

我们下面会对专门对展开方式进行一个探讨。

此外,如果我们想换行,我们可以使用 endl 。它就"相当于" \\n,都可以起到一个换行的效果。

大量输入输出时尽量少用或者不用 endl ,每次 endl 会刷新缓冲区,这增加了 io 次数,

在数据非常大的时候尽量不要用endl,转义字符不存在刷新缓冲区的问题。

即 —— 需要大量输入输出时建议用 \\n 替换 endl 。

使用 C++ 的输入输出是很方便的,它最大的特点就是可以自动识别类型。

#include <iostream>
using namespace std;int main()
{int i = 10;double d = 3.14;cout << i << " " << d << endl;   // 相当于 printf("%d %f\\n", i, d);return 0;
}

7.1.2 流提取运算符 >>

cin 后面的 >> ,我们称之为 "流提取运算符" 。

#include <iostream>
using namespace std;int main()
{int i = 0;double d = 0.0;cout << "请输入一个整数和一个小数:> ";cin >> i >> d;cout << "你输入的是:> ";cout << i << " 和 " <<  d << endl;return 0;
}

7.2 C和C++的混合使用

前面说过C++是兼容C的

因为 <iostream> 有些平台间接的包了 printf 等 C语言中的函数。

有人这时候就会觉得,C++的输入和输出可以自动识别类型,这也太爽了吧,既然 C++ 的输入输出这么好用,那我们是不是就用不上 C语言里的输入输出了?

不是,我举个简单的栗子:

如果我们想打印某个浮点数,如何控制小数点后的位数呢?在 C++ 的输入中,这是一件比较麻烦的事情。如果想控制小数点的位数,我们完全可以使用C语言的输入:printf("%.2f", d);

因此,并不是说学了C++的输入输出,我们就不用C语言的输入输出了。

C++ 和 C语言的输入输出可以混在一起写,混在一起用。

什么时候用 C++ 的,什么时候用 C语言的,看情况就可以了。

(在后面我们熟悉C++后还能用C++和C一起打面向过程和面向对象的组合拳)

struct Student
{char name[20];int age;
};int main()
{// 这种情况C++ 就不方便了struct Student s = {"xiaoming", 18};cout << "姓名:" << s.name << endl;    cout << "年龄:" << s.age << endl << endl;// 用c呢?printf("姓名:%s\\n 年龄:%d\\n", s.name, s.age);  // 所以说C语言也是有它的优势的return 0;
}

7.3 再看命名空间的展开

上面我们讨论过使用 using namespace 展开的缺陷,会失去隔离的效果。

这样写好吗?不好。这么一来就全展开来了,所以我们可以这么写:

采用方式一:空间名 + 作用域限定符 

#include <iostream>
//using namespace std;int main() 
{// cout << "hello world!\\n" << endl; std::cout << "hello world!" << std::endl;  // 指定命名空间return 0;
}

采用方式二:使用 using namespace 命名空间名称引入 (会破坏隔离效果)

#include <iostream>
using namespace std;int main()
{cout << "hello world!" << endl;return 0;
}

虽然 using namespace 会破坏隔离效果,但是我们平时写练习还是可以这么去做的。因为我们平时不需要这么过分地讲究命名空间,但是以后在写项目或干正事的时候就得讲究讲究了。

采用方式三:使用 using 将命名空间中成员引入

#include <iostream>using std::cout; // 把常用的展开
using std::endl;int main()
{cout << "hello world!" << endl;  return 0;
}

8.再看hello world!

#include <iostream>
using namespace std;int main()
{cout << "hell world!" << endl;return 0;
}

 现在是不是都能明白了?

恭喜你学会hell world!了,正式踏上了C++的学习之路!

学习C++是一个漫长的过程,当然你可以试试下面的21天学会C++

C++不仅仅是一门语言,更是一种生活哲学,在以后的工作之余还需要大家继续挖掘……

本章完。