C++ 入门基础之一

C++ 简介

简介:

  • C++ 被认为是一种中级语言,它综合了高级语言和低级语言的特点。
  • C++ 是 C 的一个超集,事实上,任何合法的 C 程序都是合法的 C++ 程序。
  • C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程。
  • C++ 是由 Bjarne Stroustrup 于 1979 年在新泽西州美利山贝尔实验室开始设计开发的。C++ 进一步扩充和完善了 C 语言,最初命名为带类的 C,后来在 1983 年更名为 C++。

注意:使用静态类型的编程语言是在编译时执行类型检查,而不是在运行时执行类型检查。

ANSI 标准:

ANSI 标准是为了确保 C++ 的便携性 —— 您所编写的代码在 Mac、UNIX、Windows、Alpha 计算机上都能通过编译。由于 ANSI 标准已稳定使用了很长的时间,所有主要的 C++ 编译器的制造商都支持 ANSI 标准。

标准 C++ 的三大组成部分:

  • 核心语言,提供了所有构件块,包括变量、数据类型和常量等。
  • C++ 标准库,提供了大量的函数,用于操作文件、字符串等。
  • 标准模板库(STL),提供了大量的方法,用于操作数据结构等。

第一个 C++ 程序

1
2
3
4
5
6
7
8
9
10
11
12
13
// 包含C++的头文件
#include <iostream>

// 使用命名空间 std(标准的命名空间),在这个命名空间中定义了很多 C++ 的标准定义
using namespace std;

int main() {
// cout: 标准输出
// endl: 换行符号,类似 "\n"
// << 左移操作符: 在C++里面,属于功能的改造(增强),即 C++ 语言的操作符重载
cout << "hello world" << endl;
return 0;
}

程序运行的输出结果如下:

1
hello world

关于 endl\n 的区别:

  • 在 C++ 中,终端输出换行时,用 cout << ... << endl\n 都可以,但二者有小小的区别,用 endl 时会刷新缓冲区,使得栈中的东西刷新一次;但用 \n 则不会刷新,它只会换行,栈内的数据没有变化。一般情况,二者的这点区别是很小的,在大型的程序中可能会用到,建议用 endl 来换行。
  • endl 除了写入 \n 之外,还会调用 flush 函数来刷新缓冲区,将缓冲区里的数据写入文件或屏幕,若考虑效率则可以直接使用 \n
  • cout << endl; 等价于 cout << '\n' << flush;

程序设计方法介绍

面向过程的程序设计方法

设计思路

面向过程的结构化程序设计方法,自顶向下、逐步求精。采用模块分解与功能抽象,自顶向下、分而治之。

程序结构

  • 按功能划分为若干个基本模块,形成一个树状结构。
  • 各模块间的关系尽可能简单,功能上相对独立;每一模块内部均是由顺序、选择和循环三种基本结构组成。
  • 其模块化实现的具体方法是使用子程序。

优缺点

优点:

  • 有效地将一个较复杂的程序系统设计任务分解成许多易于控制和处理的子任务,便于开发和维护。

缺点:

  • 可重用性差、数据安全性差、难以开发大型软件和图形界面的应用软件
  • 把数据和处理数据的过程分离为相互独立的实体。
  • 当数据结构改变时,所有相关的处理过程都要进行相应的修改。
  • 每一种相对于老问题的新方法都要带来额外的开销。
  • 图形用户界面的应用程序,很难用过程来描述和实现,开发和维护也都很困难。

面向对象的程序设计方法

C++ 完全支持面向对象的程序设计,包括面向对象开发的四大特性: 封装、抽象、继承、多态,更多特性如下:

  • 将数据及对数据的操作方法封装在一起,作为一个相互依存、不可分离的整体(对象)。
  • 对同类型对象抽象出其共性,形成类。
  • 类通过一个简单的外部接口,与外界发生关系。
  • 对象与对象之间通过消息进行通信。

面向对象的软件工程概述

面向对象的软件工程是面向对象方法在软件工程领域的全面应用,分别包括:

  • 面向对象的分析(OOA)
  • 面向对象的设计(OOD)
  • 面向对象的编程(OOP)
  • 面向对象的测试(OOT)
  • 面向对象的软件维护(OOSM)

面向过程程序设计:数据结构 + 算法,主要用于解决科学计算问题,用户需求简单而固定,其特点和劣势如下:

特点:

  • 分析解决问题所需要的步骤
  • 利用函数实现各个步骤
  • 依次调用函数解决问题

劣势:

  • 软件可重用性差
  • 软件可维护性差
  • 构建的软件无法满足用户需求

面向对象程序设计:由现实世界建立软件模型,将现实世界中的事物直接映射到程序中,可直接满足用户需求,其特点和优势如下:

特点:

  • 直接分析用户需求中涉及的各个实体
  • 在代码中描述现实世界中的实体
  • 在代码中关联各个实体协同工作解决问题

优势:

  • 构建的软件能够适应用户需求的不断变化
  • 直接利用面向过程方法的优势而避开其劣势

计算圆形的面积

面向过程的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>

using namespace std;

int main() {
double r = 0; // 圆形的半径
double s = 0; // 圆形的面积

cout << "请输入圆形的半径:";
cin >> r;
s = 3.14 * r * r;
cout << "圆形的面积是:" << s << endl;
return 0;
}

面向对象的写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

using namespace std;

class Circle {

public:
double m_r; // 圆形的半径
double m_s; // 圆形的面积

public:
void setR(double r) {
m_r = r;
}

double getR() {
return m_r;
}

double getS() {
m_s = 3.14 * m_r * m_r;
return m_s;
}

};

int main() {
double r;
cout << "请输入圆形的半径:";
cin >> r;

Circle circle;
circle.setR(r);
cout << "圆形的面积是:" << circle.getS() << endl;
return 0;
}

C++ 基础概念

命名空间

所谓 namespace,是指标识符的各种可见范围。C++ 标准程序库中的所有标识符都被定义于一个名为 stdnamespace 中。<iostream><iostream.h> 格式是不一样的,前者没有后缀,实际上在编译器 include 文件夹里面可以看到,二者是两个文件,打开文件就会发现,里面的代码是不一样的。后缀为 .h 的头文件 C++ 标准已经明确提出不再支持了,早些的实现将标准库功能定义在全局命名空间里,即声明在带 .h 后缀的头文件里;C++ 标准为了和 C 区别开,也为了正确使用命名空间,规定头文件不再使用后缀 .h

<iostream.h><iostream> 的区别:

  • 当使用 <iostream.h> 时,相当于在 C 中调用库函数,使用的是全局命名空间,也就是早期的 C++ 实现
  • 当使用 <iostream> 的时候,该头文件没有定义在全局命名空间,必须使用 using namespace std; 这样才能正确使用 cout 等关键字

由于 namespace 的概念,使用 C++ 标准程序库的任何标识符时,可以有以下三种写法可选择:

  • 直接指定标识符:例如 std::ostream 而不是 ostream,完整语句为: std::cout << std::hex << 3.4 << std::endl;
  • 使用 using 关键字using std::cout; using std::endl; using std::cin;,以上语句可以写成 cout << hex << 3.4 << endl;
  • 使用 using namespace std:这种写法是最方便的,例如: using namespace std;,以上语句可以写成 cout << hex << 3.4 << endl;

命名空间 std 内定义的所有标识符都有效(曝光),就好像它们被声明为全局变量一样,那么以上语句就可以这样写 cout << hex << 3.4 << endl;。因为标准库非常的庞大,所以程序员在选择的类的名称或函数名时就很有可能和标准库中的某个名字相同。因此为了避免这种情况所造成的名字冲突,就把标准库中的一切都被放在名字空间 std 中。但这又会带来了一个新问题,无数原有的 C++ 代码都依赖于使用了多年的伪标准库中的功能,它们都是在全局命名空间下的。所以就有了 <iostream.h><iostream> 等等这样的头文件,一个是为了兼容以前的 C++ 代码,另一个是为了支持新的标准。命名空间 std 封装的是标准程序库的名称,标准程序库为了和以前的头文件区别,一般不加后缀 .h

命名空间定义及使用语法

在 C++ 中,名称(name)可以是符号常量、变量、宏、函数、结构、枚举、类和对象等等。为了避免在大规模程序的设计中,以及在程序员使用各种各样的 C++ 库时,这些标识符的命名发生冲突,标准 C++ 引入了关键字 namespace(命名空间 / 名字空间 / 名称空间 / 名域),这样就可以更好地控制标识符的作用域。std 是 C++ 标准命名空间,C++ 标准程序库中的所有标识符都被定义在 std 中,比如标准库中的类 iostreamvector 等都定义在该命名空间中,使用时要加上 using 声明(如 using namespace std) 或者 using 指示(如 std::stringstd::vector<int>)。

C 语言中的命名空间:

  • 标识符之间可能发生冲突
  • 在 C 语言中只有一个全局作用域
  • C 语言中所有的全局标识符共享同一个作用域

C++ 中的命名空间:

  • 命名空间可以相互嵌套定义
  • 全局作用域也叫默认命名空间
  • 命名空间将全局作用域分成不同的部分
  • 不同命名空间中的标识符可以同名而不会发生冲突

C++ 命名空间定义及使用语法:

  • 命名空间定义的语法:namespace name { … }
  • 命名空间使用的语法:using namespace name;
  • 使用特定命名空间中的变量:using name::variable;
  • 使用默认命名空间中的变量:::variable
  • 默认情况下可以直接使用默认命名空间中的所有标识符

命名空间编程实战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <iostream>

using namespace std;

// 定义命名空间 NameSpaceA
namespace NameSpaceA {
int a = 0;
}

// 定义命名空间 NameSpaceB
namespace NameSpaceB {
int a = 1;

// 嵌套定义命名空间 NameSpaceC
namespace NameSpaceC {
struct Teacher {
char name[10];
int age;
};
}
}

int main() {
// 声明 std 命名空间后的写法
cout << "hello world" << endl;

// 不声明 std 命名空间后的写法
std::cout << "hello world" << std::endl;

// 声明 NameSpaceA 命名空间
using namespace NameSpaceA;

// 使用 NameSpaceC 命名空间中的变量
using NameSpaceB::NameSpaceC::Teacher;

printf("a = %d\n", a);
printf("a = %d\n", NameSpaceB::a);

Teacher teacher = {"Jim", 20};
printf("teacher.age = %d\n", teacher.age);
printf("teacher.name = %s\n", teacher.name);

return 0;
}

程序运行的输出结果如下:

1
2
3
4
5
6
hello world
hello world
a = 0
a = 1
teacher.age = 20
teacher.name = Jim

C 语言和 C++ 的关系

C 语言是在实践的过程中逐步完善起来的,没有深思熟虑的设计过程,使用时存在很多 灰色地带,残留量过多低级语言的特征,直接利用指针进行内存操作,其最终目标是程序执行效率的高效。当面向过程方法论暴露越来越多的缺陷的时候,业界开始考虑在工程项目中引入面向对象的设计方法,而第一个需要解决的问题就是:高效的面向对象语言,并且能够兼容已经存在的代码。C 语言和 C++ 语言的关系如下:

  • C 语言和 C++ 并不是对立的竞争关系
  • C++ 是 C 语言的加强,是一种更好的 C 语言
  • C++ 是以 C 语言为基础的,并且完全兼容 C 语言的特性
  • C 语言 + 面向对象方法论 —> C++ / Objective C

C++ 对 C 语言的增强

实用性增强

C 语言中的变量都必须在作用域开始的位置定义,而 C++ 中更强调语言的 实用性,所有的变量都可以在需要使用时再定义。

1
2
3
4
5
6
7
int main(int argc, char *argv[])
{
int a = 0;
printf("hello world\n");
int b = 13; // C语言编译器中编译报错,但是C++编译器中不会报错
return 0;
}

变量检测增强

在 C 语言中,重复定义多个同名的全局变量是合法的,但在 C++ 中,不允许定义多个同名的全局变量。C 语言中多个同名的全局变量最终会被链接到全局数据区的同一个地址空间上。

1
2
int g_var;
int g_var = 1; // C++直接拒绝这种二义性的做法

struct 类型的增强

C 语言的 struct 定义了一组变量的集合,C 编译器并不认为这是一种新的类型,而在 C++ 中的 struct 是一个新类型的定义声明。

1
2
3
4
5
6
7
8
9
10
11
12
struct Student
{
char name[100];
int age;
};

int main(int argc, char *argv[])
{
Student s1 = {"wang", 1}; // C语言编译器编译报错,C++编译器编译通过
struct Student s2 = {"chen", 1}; // C语言编译器编译通过
return 0;
}

register 关键字增强

register 关键字的作用是请求编译器尽可能地将变量存在 CPU 内部的寄存器中,而不是通过内存寻址访问,以提高程序运行效率。注意这里是尽可能,不是绝对。首先,register 变量必须是能被 CPU 所接受的类型,这通常意味着 register 变量必须是一个单个的值,并且长度应该小于或者等于整型的长度。不过,有些机器的寄存器也能存放浮点数。C 语言中,register 关键字表示 “请求”(不一定成功)让变量直接放进寄存器中,方便访问,但是在 C 语言中不能取该变量的地址。C++ 对 register 进行了增强,C++ 编译器会对频繁被调用的变量主动申请为 register,即使没有用 register 关键字声明,它也会这样做。值得一提的是,C++ 编译器当发现程序中需要对 register 变量取地址时,register 对变量的声明会变得无效。

1
2
3
4
5
6
int main(int argc, char *argv[])
{
register int a = 0;
printf("&a = %x\n", &a);
return 0;
}

新增 Bool 类型关键字

C++ 在 C 语言的基本类型系统之上增加了 bool 类型关键字,bool 可取的值只有 truefalse。理论上 bool 变量只占用一个字节,如果多个 bool 变量定义在一起,可能会各占一个 bit(位),这取决于编译器的实现。true 代表真值,编译器内部用 1 来表示,false 代表非真值,编译器内部用 0 来表示。C++ 编译器会在赋值时将非 0 值转换为 true0 值转换为 false

1
2
3
4
5
6
7
int main(int argc, char *argv[])
{
int a;
bool b = true;
printf("b = %d, sizeof(b) = %d\n", b, sizeof(b));
return 0;
}

程序运行的输出结果如下:

1
b = 1, sizeof(b) = 1

三目运算符功能增强

1
2
3
4
5
6
7
8
9
10
int main(int argc, char *argv[])
int a = 10;
int b = 20;

// 返回一个最小数,并且给最小数赋值成30
// C 语言中三目运算符是一个表达式 ,表达式不可以做左值,而 C++ 则可以
(a < b ? a : b) = 30;
printf("a = %d, b = %d\n", a, b);
return 0;
}

程序运行的结果如下:

1
a = 30, b = 20
  • 使用三目运算符时,C 语言返回变量的值,C++ 是返回变量本身
  • C 语言中的三目运算符返回的是变量值,不能作为左值使用
  • C++ 中的三目运算符可直接返回变量本身,因此可以出现在程序的任何地方
  • 特别注意:C++ 中三目运算符可能返回的值中如果有一个是常量值,则不能作为左值使用,例如 (a < b ? 1 : b )= 30;
  • C 语言如何支持类似 C++ 的三目运算特性呢?当左值的条件:要有内存空间,而 C++ 编译器只是帮助程序员取了一个地址而已,C 语言版的写法为:*(a < b ? &a : &b) = 30

所有的变量和函数都必须声明类型

C 语言默认数据类型在 C++ 编译器中是不合法的,C++ 中所有变量和函数必须声明类型。以下代码在 C 语言中能编译通过,但在 C++ 中会编译报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
f(i)
{
printf("i = %d\n", i);
}

g()
{
return 5;
}

int main(int argc, char *argv[])
{
f(10);
printf("g() = %d\n", g(1, 2, 3, 4, 5));
getchar();
return 0;
}
  • 在 C 语言中,int f() 表示返回值为 int,接受任意参数的函数
  • 在 C 语言中,int f(void) 表示返回值为 int 的无参函数
  • 在 C++ 中,int f()int f(void) 具有相同的意义,都表示返回值为 int 的无参函数
  • C++ 更加强调类型,任意的程序元素都必须显示指明类型