C++基础
1.1 介绍和环境设置
1.1.1 C++简介
C++是一种通用编程语言,支持过程化编程、面向对象编程以及泛型编程。它由Bjarne Stroustrup在20世纪80年代早期在贝尔实验室开发,是对C语言的扩展。C++增加了类和对象的概念,以支持面向对象编程,并添加了一些其他功能,如异常处理和模板等。
1.1.2 环境设置搭建讲解
Windows环境:在Windows上,你可以使用IDE(如Visual Studio,Code::Blocks等)进行C++编程,这些IDE通常都内置了C++编译器。另外,你也可以选择手动安装C++编译器,比如MinGW。
Linux环境:在Linux上,GCC编译器是最常用的C++编译器。你可以通过包管理器(如apt-get或yum)来安装GCC。
Mac环境:在Mac上,你可以通过安装Xcode来获取C++编译器。
1.1.3 练习题
练习1:安装你选择的C++开发环境,并确保你能够编译和运行一个简单的"Hello, World!"程序。
1.1.4 项目实践
项目1:创建一个简单的C++项目,比如一个可以执行基本数学运算的计算器程序。这个项目旨在让你熟悉C++的编程环境和工具,以及基本的编译和运行流程。
1.2 基本语法
1.2.1 理论讲解
C++语言的基本语法和C语言非常相似。一个C++程序主要由以下部分构成:头文件、预处理器指令、主函数、变量声明、表达式、语句和注释。
头文件:这是C++程序的一部分,其中包含了类和函数的定义。例如,#include <iostream>
就是一个头文件,它包含了进行输入/输出操作的函数。
预处理器指令:这些指令在编译开始之前由预处理器进行处理。例如,#include
就是一种预处理器指令,用于引入头文件。
主函数:C++程序的执行从主函数开始。一个简单的主函数如下所示:
int main() {
// 代码
}
变量声明:在C++中,变量必须在使用之前声明。例如,int a;
就是一个变量声明,表示声明了一个类型为整型的变量a。
表达式:表达式是一种计算公式,可以生成一个值。例如,a + b
就是一个表达式。
语句:语句是执行特定任务的指令。在C++中,每个语句后面都要加上分号。例如,a = b + c;
就是一个语句。
注释:注释是用于解释代码的文本,不会被编译器执行。C++中有两种类型的注释,单行注释(//)和多行注释(/* … */)。
1.2.2 示例代码
以下是一个简单的C++程序,展示了基本的语法:
#include <iostream>
int main() {
int a = 5;
int b = 10;
int sum = a + b;
std::cout << "The sum of a and b is: " << sum << std::endl;
return 0;
}
1.2.3 练习题
练习1:编写一个C++程序,计算并打印两个整数的乘积。
1.2.4 项目实践
项目1:创建一个C++项目,比如一个简单的猜数字游戏。这个项目应该包含用户输入、变量处理、条件语句等基本语法元素。
1.3 数据类型
1.3.1 理论讲解
在C++中,数据类型定义了变量或对象可以存储的数据的类型和大小。C++中的数据类型可以分为以下几种:
-
基本数据类型:包括整型(int)、浮点型(float)、双精度浮点型(double)、字符型(char)和布尔型(bool)。
-
复合数据类型:包括数组、函数、指针、引用、类和对象等。
-
用户自定义数据类型:如结构(struct)、联合(union)和枚举(enum)。
1.3.2 示例代码
下面是一个演示不同数据类型的简单C++程序:
#include <iostream>
int main() {
int integer = 10; // integer
float decimal = 10.5; // floating point
double precise_decimal = 10.55555; // double
char character = 'A'; // character
bool is_true = true; // boolean
std::cout << "Integer: " << integer << std::endl;
std::cout << "Float: " << decimal << std::endl;
std::cout << "Double: " << precise_decimal << std::endl;
std::cout << "Character: " << character << std::endl;
std::cout << "Boolean: " << std::boolalpha << is_true << std::endl;
return 0;
}
1.3.3 练习题
练习1:声明和初始化以下数据类型的变量,并打印它们的值:int、float、double、char和bool。
1.3.4 项目实践
项目1:创建一个C++项目,比如一个简单的学生信息管理系统。这个项目应该包含各种数据类型的使用,包括但不限于整型(用于存储学生ID)、浮点型(用于存储学生成绩)、字符型(用于存储性别)和字符串(用于存储学生姓名)。
1.4 变量和常量
1.4.1 理论讲解
变量:在C++中,变量是用来存储数据的容器。变量的名称可以由字母、数字和下划线组成,但必须以字母或下划线开始。C++是一种静态类型的语言,因此在声明变量时必须指定其类型。
常量:在C++中,常量是固定值的标识符。它们的值在定义后不能改变。你可以使用const
关键字来定义常量。
1.4.2 示例代码
以下是一段简单的C++代码,演示了如何定义和使用变量和常量:
#include <iostream>
int main() {
int var = 10; // variable
const int const_var = 20; // constant
std::cout << "Variable: " << var << std::endl;
std::cout << "Constant: " << const_var << std::endl;
var = 30; // change the value of variable
// const_var = 40; // error: cannot change the value of constant
std::cout << "Variable: " << var << std::endl;
return 0;
}
1.4.3 练习题
练习1:声明和初始化一个整型变量和一个整型常量,然后尝试修改它们的值。
1.4.4 项目实践
项目1:创建一个C++项目,比如一个简单的银行账户管理系统。在这个项目中,你需要使用变量来存储账户的信息(如账户余额),并使用常量来表示一些固定的信息(如银行利率)。
1.5 运算符
1.5.1 理论讲解
C++中的运算符用于执行数据的运算和操作。C++中的运算符主要包括以下几种:
-
算术运算符:如加(+)、减(-)、乘(*)、除(/)和取余(%)。
-
赋值运算符:如等号(=)、加等(+=)、减等(-=)、乘等(*=)、除等(/=)和模等(%=)。
-
比较运算符:如等于(==)、不等于(!=)、大于(>)、小于(<)、大于等于(>=)和小于等于(<=)。
-
逻辑运算符:如与(&&)、或(||)和非(!)。
1.5.2 示例代码
以下是一个简单的C++程序,演示了不同运算符的使用:
#include <iostream>
int main() {
int a = 10;
int b = 20;
std::cout << "a + b = " << a + b << std::endl;
std::cout << "a - b = " << a - b << std::endl;
std::cout << "a * b = " << a * b << std::endl;
std::cout << "a / b = " << a / b << std::endl;
std::cout << "a % b = " << a % b << std::endl;
std::cout << "a == b: " << (a == b) << std::endl;
std::cout << "a != b: " << (a != b) << std::endl;
std::cout << "a > b: " << (a > b) << std::endl;
std::cout << "a < b: " << (a < b) << std::endl;
bool x = true;
bool y = false;
std::cout << "x && y: " << (x && y) << std::endl;
std::cout << "x || y: " << (x || y) << std::endl;
std::cout << "!x: " << (!x) << std::endl;
return 0;
}
1.5.3 练习题
练习1:编写一个C++程序,使用算术运算符、比较运算符和逻辑运算符进行一些基本的运算。
1.5.4 项目实践
项目1:创建一个C++项目,如一个简单的计算器程序。这个程序应该接受用户输入的两个数字,然后进行加、减、乘、除等运算,并打印结果。
1.6 控制结构(条件语句、循环语句)
1.6.1 理论讲解
在C++中,你可以使用条件语句和循环语句来控制程序的流程。
条件语句:条件语句用于基于特定条件执行特定的代码段。C++提供了以下几种类型的条件语句:if语句、if-else语句、嵌套if语句、switch语句。
循环语句:循环语句允许你多次执行一段代码。C++提供了以下几种类型的循环:while循环、do-while循环、for循环、嵌套循环。
1.6.2 示例代码
以下是一段简单的C++代码,演示了条件语句和循环语句的使用:
#include <iostream>
int main() {
// if-else statement
int a = 10;
if (a > 5) {
std::cout << "a is greater than 5" << std::endl;
} else {
std::cout << "a is less than or equal to 5" << std::endl;
}
// for loop
for (int i = 0; i < 5; i++) {
std::cout << i << std::endl;
}
// while loop
int b = 0;
while (b < 5) {
std::cout << b << std::endl;
b++;
}
// do-while loop
int c = 0;
do {
std::cout << c << std::endl;
c++;
} while (c < 5);
return 0;
}
1.6.3 练习题
练习1:编写一个C++程序,使用if-else语句根据一个分数决定成绩的等级。
练习2:编写一个C++程序,使用for循环打印出1到10的数字。
练习3:编写一个C++程序,使用while或do-while循环来实现一个简单的计数器。
1.6.4 项目实践
项目1:创建一个C++项目,如一个简单的猜数字游戏。在这个游戏中,计算机随机生成一个数字,然后让用户尝试猜测这个数字。你需要使用条件语句来判断用户的猜测是否正确,如果猜错,给出提示;如果猜对,游戏结束。同时,你还需要使用循环语句来确保游戏可以持续进行,直到用户猜对为止。
函数与程序结构
2.1 函数定义和调用
2.1.1 理论讲解
函数是一段可以被程序多次调用的代码块。在C++中,函数包含一个函数头和一个函数体。函数头包括函数名、返回类型以及参数列表。函数体包括一系列定义函数行为的语句。
2.1.2 示例代码
以下是一个简单的C++程序,定义并调用了一个函数:
#include <iostream>
// function declaration
int add(int a, int b);
int main() {
int result = add(5, 10); // function call
std::cout << "The sum is: " << result << std::endl;
return 0;
}
// function definition
int add(int a, int b) {
return a + b;
}
在这个例子中,add
是一个函数,它接收两个整数作为参数,返回它们的和。
2.1.3 练习题
练习1:编写一个C++函数,计算并返回两个数字的乘积。
练习2:编写一个C++函数,检查一个数字是否为偶数。
2.1.4 项目实践
项目1:创建一个C++项目,例如一个简单的数学函数库。这个库可以包含一些基本的数学函数,如加、减、乘、除、平方、开方等。每个函数都应该被正确地定义和实现,以便可以在程序中被多次调用。
2.2 递归函数
2.2.1 理论讲解
递归函数是一种在函数体内部调用自身的函数。递归通常用来解决那些可以被分解成更小问题的问题。一个递归函数通常包含两部分:基线条件(函数停止调用自身的条件)和递归条件(函数继续调用自身的条件)。
2.2.2 示例代码
以下是一个简单的C++程序,它定义并使用了一个递归函数来计算阶乘:
#include <iostream>
// function declaration
int factorial(int n);
int main() {
int num = 5;
int result = factorial(num); // function call
std::cout << num << "! is: " << result << std::endl;
return 0;
}
// function definition
int factorial(int n) {
if (n == 0) { // base case
return 1;
} else { // recursive case
return n * factorial(n - 1);
}
}
在这个例子中,factorial
是一个递归函数,它计算一个数的阶乘。
2.2.3 练习题
练习1:编写一个C++函数,使用递归方法计算斐波那契数列的第n项。
练习2:编写一个C++函数,使用递归方法实现字符串的反转。
2.2.4 项目实践
项目1:创建一个C++项目,例如一个简单的递归问题集合。这个项目可以包含一些常见的递归问题,如汉诺塔问题、迷宫问题等。每个问题都应该由一个递归函数来解决。
2.3 函数参数
2.3.1 理论讲解
在C++中,函数参数是用来从函数调用者向函数传递信息的。参数可以是任何类型,包括基本类型如int、float等,也可以是复杂类型如数组、结构等。函数参数在函数声明和定义中被声明,并在函数调用时被赋值。C++主要有两种参数传递方式:
-
传值调用:这是默认的参数传递方式。在这种方式下,函数的参数在调用函数时被初始化为提供的值或变量的拷贝。在函数内部对参数的任何更改都不会影响到调用函数时传入的原始数据。
-
引用调用:在这种方式下,函数的参数在调用函数时被初始化为提供的变量的引用。在函数内部对参数的任何更改都会影响到调用函数时传入的原始数据。
2.3.2 示例代码
以下是一个简单的C++程序,演示了传值调用和引用调用的区别:
#include <iostream>
// function declarations
void passByValue(int a);
void passByReference(int &a);
int main() {
int num = 10;
passByValue(num); // function call
std::cout << "After passByValue, num is: " << num << std::endl;
passByReference(num); // function call
std::cout << "After passByReference, num is: " << num << std::endl;
return 0;
}
// function definitions
void passByValue(int a) {
a += 10;
}
void passByReference(int &a) {
a += 10;
}
在这个例子中,函数passByValue
通过值传递参数,所以在函数内部对参数的修改并不影响原始变量的值。而函数passByReference
通过引用传递参数,所以在函数内部对参数的修改会改变原始变量的值。
2.3.3 练习题
练习1:编写一个C++函数,使用传值方式交换两个数字。
练习2:编写一个C++函数,使用引用方式交换两个数字。
2.3.4 项目实践
项目1:创建一个C++项目,例如一个简单的排序算法库。在这个库中,你可以实现一些常见的排序算法,如冒泡排序、选择排序、插入排序等。每个算法都应该被定义为一个函数,这些函数接收一个数组和数组的长度作为参数,并使用引用或指针方式在函数内部修改数组,以实现排序功能。
2.4 内联函数
2.4.1 理论讲解
在C++中,内联函数是一种特殊类型的函数,它在编译时会被插入到每个调用它的地方,而不是通常的函数调用方式。内联函数的主要目的是提高程序的运行速度,因为函数调用过程的开销可以被消除。但这也会增加程序的大小,因为函数的代码会被复制到每个调用的地方。一般情况下,适合作为内联函数的是那些代码简单、执行速度快、调用频率高的小函数。
要声明内联函数,只需要在函数声明前加上inline
关键字。
2.4.2 示例代码
以下是一个简单的C++程序,演示了内联函数的使用:
#include <iostream>
// inline function declaration
inline int square(int a);
int main() {
int num = 5;
int result = square(num); // function call
std::cout << "The square of " << num << " is: " << result << std::endl;
return 0;
}
// inline function definition
inline int square(int a) {
return a * a;
}
在这个例子中,square
是一个内联函数,它返回一个数的平方。
2.4.3 练习题
练习1:编写一个内联函数,计算并返回一个数字的立方。
练习2:编写一个内联函数,检查一个数字是否是偶数。
2.4.4 项目实践
项目1:创建一个C++项目,例如一个简单的数学函数库。这个库可以包含一些常用的数学计算,例如求平方、求立方、求阶乘等。每个函数都应该被声明为内联函数,以提高程序的运行效率。
2.5 默认参数
2.5.1 理论讲解
在C++中,函数参数可以有默认值。当函数被调用时,如果没有提供某个参数的值,那么将使用这个参数的默认值。默认参数的声明必须在函数声明中,而不能在函数定义中。
2.5.2 示例代码
以下是一个简单的C++程序,演示了默认参数的使用:
#include <iostream>
// function declaration with default arguments
int add(int a, int b = 0);
int main() {
int result1 = add(5, 10); // function call with two arguments
std::cout << "The sum of 5 and 10 is: " << result1 << std::endl;
int result2 = add(5); // function call with one argument
std::cout << "The sum of 5 and 0 is: " << result2 << std::endl;
return 0;
}
// function definition
int add(int a, int b) {
return a + b;
}
在这个例子中,函数add
有两个参数,其中第二个参数有一个默认值0。所以你可以使用一个参数或两个参数来调用这个函数。
2.5.3 练习题
练习1:编写一个C++函数,它接受两个参数,第二个参数有一个默认值,并返回两个参数的积。
练习2:编写一个C++函数,它接受一个字符串和一个数字作为参数,数字参数有一个默认值,函数的功能是将字符串重复指定次数。
2.5.4 项目实践
项目1:创建一个C++项目,例如一个简单的字符串处理库。这个库可以包含一些常见的字符串操作,如查找、替换、分割等。一些函数可以接受可选的参数,这些参数在函数声明中提供默认值。
数组与字符串
3.1 数组的定义与使用
3.1.1 理论讲解
在C++中,数组是用来存储相同类型数据的集合。数组的大小必须在声明时确定,并且不能更改。数组中的每个元素都有一个索引,索引从0开始,并且可以用来访问和修改元素的值。
3.1.2 示例代码
以下是一个简单的C++程序,演示了数组的定义和使用:
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5}; // array declaration and initialization
// accessing array elements
for(int i = 0; i < 5; i++) {
std::cout << "Element at index " << i << " is: " << arr[i] << std::endl;
}
// modifying array elements
arr[2] = 6;
std::cout << "After modification, element at index 2 is: " << arr[2] << std::endl;
return 0;
}
在这个例子中,我们声明并初始化了一个整数数组arr
。然后,我们通过索引访问和修改数组中的元素。
3.1.3 练习题
练习1:编写一个C++程序,声明并初始化一个字符串数组,然后打印出数组中的所有元素。
练习2:编写一个C++程序,声明并初始化一个整数数组,然后找出并打印出数组中的最大元素和最小元素。
3.1.4 项目实践
项目1:创建一个C++项目,例如一个简单的数组处理库。这个库可以包含一些常见的数组操作,如查找、排序、翻转等。每个操作都应该被定义为一个函数,这些函数接收一个数组和数组的长度作为参数。
3.2 字符串处理
3.2.1 理论讲解
在C++中,字符串是一个字符数组,由一个或多个字符组成,并以空字符’\0’结束。C++标准库中的string类提供了一系列操作字符串的方法,如获取字符串长度、连接字符串、提取字符串子串等。
3.2.2 示例代码
以下是一个简单的C++程序,演示了string类的一些基本用法:
#include <iostream>
#include <string>
int main() {
std::string str1 = "Hello"; // string declaration and initialization
std::string str2 = " World";
// string concatenation
std::string str3 = str1 + str2;
std::cout << "After concatenation, str3 is: " << str3 << std::endl;
// get string length
int length = str3.length();
std::cout << "The length of str3 is: " << length << std::endl;
// get substring
std::string substr = str3.substr(6, 5);
std::cout << "The substring of str3 from index 6 with length 5 is: " << substr << std::endl;
return 0;
}
在这个例子中,我们声明并初始化了两个字符串str1
和str2
,然后我们通过+
运算符将两个字符串连接成一个新的字符串str3
。我们还使用了length
方法获取字符串的长度,以及substr
方法获取字符串的子串。
3.2.3 练习题
练习1:编写一个C++程序,输入一个字符串,然后反转并打印出这个字符串。
练习2:编写一个C++程序,输入两个字符串,然后判断这两个字符串是否相等。
3.2.4 项目实践
项目1:创建一个C++项目,例如一个简单的字符串处理库。这个库可以包含一些常见的字符串操作,如查找、替换、分割、转换为大写或小写等。每个操作都应该被定义为一个函数,这些函数接收一个或多个字符串作为参数。
3.3 多维数组
3.3.1 理论讲解
在C++中,可以声明和使用多维数组,最常见的是二维数组。二维数组可以被看作是一个表格,有行和列。二维数组在声明时需要指定行数和列数,并且可以在声明时初始化。访问二维数组中的元素需要使用两个索引,第一个索引表示行,第二个索引表示列。
3.3.2 示例代码
以下是一个简单的C++程序,演示了二维数组的定义和使用:
#include <iostream>
int main() {
// 2D array declaration and initialization
int arr[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// accessing 2D array elements
for(int i = 0; i < 2; i++) {
for(int j = 0; j < 3; j++) {
std::cout << "Element at position (" << i << ", " << j << ") is: " << arr[i][j] << std::endl;
}
}
return 0;
}
在这个例子中,我们声明并初始化了一个2x3的二维数组arr
。然后,我们通过两个索引访问二维数组中的每个元素。
3.3.3 练习题
练习1:编写一个C++程序,声明并初始化一个二维字符串数组,然后打印出数组中的所有元素。
练习2:编写一个C++程序,声明并初始化一个二维整数数组,然后找出并打印出数组中的最大元素和最小元素。
3.3.4 项目实践
项目1:创建一个C++项目,例如一个简单的矩阵处理库。这个库可以包含一些常见的矩阵操作,如矩阵加法、矩阵乘法、转置等。每个操作都应该被定义为一个函数,这些函数接收一个或多个二维数组(即矩阵)作为参数。
指针和引用
4.1 指针基础
4.1.1 理论讲解
在C++中,指针是一种特殊的变量,它存储的是另一个变量的内存地址,而不是一个普通的数据值。声明指针时需要指定指针所指向的数据的类型。使用&
运算符可以获取一个变量的地址,使用*
运算符可以访问指针指向的变量的值。
4.1.2 示例代码
以下是一个简单的C++程序,演示了指针的基本用法:
#include <iostream>
int main() {
int a = 5; // a normal integer variable
// declare a pointer variable and assign the address of 'a' to it
int* p = &a;
std::cout << "The value of 'a' is: " << a << std::endl;
std::cout << "The address of 'a' is: " << &a << std::endl;
std::cout << "The value of 'p' (which is the address of 'a') is: " << p << std::endl;
std::cout << "The value that 'p' points to is: " << *p << std::endl;
return 0;
}
在这个例子中,我们声明了一个整数变量a
和一个指针变量p
。p
存储的是a
的地址,因此*p
的值就是a
的值。
4.1.3 练习题
练习1:编写一个C++程序,声明一个整数变量和一个指针变量,将整数变量的地址赋给指针变量,然后通过指针修改整数变量的值,并验证结果。
练习2:编写一个C++程序,声明一个整数数组和一个指针,将数组的地址赋给指针,然后通过指针访问和修改数组中的元素,并验证结果。
4.1.4 项目实践
项目1:创建一个C++项目,例如一个简单的动态数组库。这个库可以包含一些常见的数组操作,如添加元素、删除元素、查找元素等。在这个库中,数组的长度可以动态地增加或减少,这需要通过指针和动态内存分配来实现。
4.2 指针和数组
4.2.1 理论讲解
在C++中,数组名可以被当做是指向数组第一个元素的指针。通过数组名和偏移量,我们可以获取和修改数组中的元素。而且,指针的运算(如加法和减法)可以被用来移动指针的指向位置。
4.2.2 示例代码
以下是一个简单的C++程序,演示了如何使用指针和数组:
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5}; // an integer array
// a pointer pointing to the first element of the array
int* p = arr;
for(int i = 0; i < 5; i++) {
std::cout << "The value at index " << i << " is: " << *(p + i) << std::endl;
}
return 0;
}
在这个例子中,我们声明了一个整数数组arr
和一个指针p
,p
指向数组的第一个元素。然后,我们通过指针和偏移量访问数组中的每个元素。
4.2.3 练习题
练习1:编写一个C++程序,声明一个整数数组和一个指针,将数组的地址赋给指针,然后通过指针和偏移量打印出数组中的所有元素。
练习2:编写一个C++程序,声明一个字符串数组和一个指针,将数组的地址赋给指针,然后通过指针和偏移量打印出数组中的所有字符串。
4.2.4 项目实践
项目1:创建一个C++项目,例如一个简单的排序算法库。在这个库中,你可以实现一些常见的排序算法,如冒泡排序、插入排序、选择排序等。在实现这些排序算法时,你可以使用指针和数组,而不是使用数组索引。
4.3 指针和函数
4.3.1 理论讲解
在C++中,函数可以接收指针类型的参数,这样就可以直接修改传入的参数的值,而不仅仅是复制参数的值。这种方式被称为传址调用。此外,函数也可以返回指针类型的值,但是需要注意的是,不要返回局部变量的地址,因为一旦函数执行完毕,局部变量的内存空间就会被回收。
4.3.2 示例代码
以下是一个简单的C++程序,演示了如何在函数中使用指针:
#include <iostream>
// a function that accepts a pointer as parameter
void updateValue(int* p) {
*p = *p * *p; // square the value that 'p' points to
}
int main() {
int a = 5;
// call the function with the address of 'a'
updateValue(&a);
std::cout << "The value of 'a' after calling updateValue is: " << a << std::endl;
return 0;
}
在这个例子中,我们定义了一个函数updateValue
,它接收一个指针作为参数,并通过这个指针修改传入的参数的值。在main
函数中,我们通过传入a
的地址来调用updateValue
函数,结果是a
的值被修改了。
4.3.3 练习题
练习1:编写一个C++程序,定义一个函数,这个函数接收一个整数数组和数组的大小作为参数,并通过指针反转数组中的元素。
练习2:编写一个C++程序,定义一个函数,这个函数接收一个字符串(字符数组)作为参数,并通过指针计算并返回字符串的长度。
4.3.4 项目实践
项目1:创建一个C++项目,例如一个简单的数据处理库。这个库可以包含一些常见的数据处理函数,如求和、求平均值、求最大值、求最小值等。这些函数都应该接收一个指向数据数组的指针和数组的大小作为参数。
4.4 引用的定义与使用
4.4.1 理论讲解
在C++中,引用是另一种类型的“指针”,它是一个已存在对象的别名。它提供了另一种方式访问已存在的变量。一旦一个引用被定义并初始化,就不能改变其所引用的对象。
4.4.2 示例代码
以下是一个简单的C++程序,演示了如何定义和使用引用:
#include <iostream>
int main() {
int a = 5;
int& ref = a; // 'ref' is a reference to 'a'
std::cout << "The value of 'a' is: " << a << std::endl;
std::cout << "The value of 'ref' is: " << ref << std::endl;
ref = 10; // change the value of 'a' through 'ref'
std::cout << "The value of 'a' is: " << a << std::endl;
std::cout << "The value of 'ref' is: " << ref << std::endl;
return 0;
}
在这个例子中,我们声明了一个整数变量a
和一个对a
的引用ref
。通过引用ref
修改a
的值,结果表明a
的值确实被改变了。
4.4.3 练习题
练习1:编写一个C++程序,定义一个函数,这个函数接收一个整数的引用作为参数,并通过这个引用修改传入的参数的值。
练习2:编写一个C++程序,定义一个函数,这个函数接收一个数组的引用和数组的大小作为参数,并通过这个引用修改数组中的所有元素。
4.4.4 项目实践
项目1:创建一个C++项目,例如一个简单的数学库。这个库可以包含一些常见的数学函数,如交换两个数、排序等。在这些函数中,可以使用引用来替代指针,以改变函数参数的值。
面向对象编程
5.1 类和对象的定义
5.1.1 理论讲解
C++是一种面向对象的编程语言,其核心概念之一就是类和对象。类是一种自定义的数据类型,它定义了一组数据和操作这些数据的函数。对象是类的实例,每个对象都有其自己的数据和函数。
5.1.2 示例代码
以下是一个简单的C++程序,演示了如何定义一个类和创建一个对象:
#include <iostream>
#include <string>
// define a class named 'Person'
class Person {
public:
// data
std::string name;
int age;
// function
void introduce() {
std::cout << "Hi, I'm " << name << ". I'm " << age << " years old." << std::endl;
}
};
int main() {
// create an object of 'Person'
Person person;
person.name = "Alice";
person.age = 20;
person.introduce(); // call the function
return 0;
}
在这个例子中,我们定义了一个类Person
,这个类有两个数据(name
和age
)和一个函数(introduce
)。然后,我们创建了一个Person
的对象,并通过这个对象访问和修改数据,以及调用函数。
5.1.3 练习题
练习1:编写一个C++程序,定义一个类,这个类包含一些数据和函数,然后创建一个这个类的对象,并通过这个对象访问和修改数据,以及调用函数。
练习2:编写一个C++程序,定义一个类,这个类包含一个构造函数和一个析构函数,然后创建一个这个类的对象,观察构造函数和析构函数的调用顺序。
5.1.4 项目实践
项目1:创建一个C++项目,例如一个简单的学生信息管理系统。在这个系统中,你可以定义一个Student
类,这个类包含一些数据,如姓名、学号、成绩等,以及一些函数,如介绍自己、计算平均成绩等。然后,你可以创建一些Student
的对象,并通过这些对象进行各种操作。
5.2 构造函数和析构函数
5.2.1 理论讲解
在C++中,构造函数和析构函数是两种特殊的成员函数,它们在对象的生命周期的开始和结束时自动被调用。
构造函数用于初始化对象的状态。它的名称与类名相同,且不返回任何值。如果没有提供构造函数,C++编译器会提供一个默认的构造函数。
析构函数用于清理对象占用的资源。它的名称是在类名前加上一个波浪符(~)来表示,且也不返回任何值。如果没有提供析构函数,C++编译器也会提供一个默认的析构函数。
5.2.2 示例代码
以下是一个简单的C++程序,演示了如何定义和使用构造函数和析构函数:
#include <iostream>
#include <string>
// define a class named 'Person'
class Person {
public:
// data
std::string name;
int age;
// constructor
Person() {
std::cout << "Person constructor is called." << std::endl;
}
// destructor
~Person() {
std::cout << "Person destructor is called." << std::endl;
}
// function
void introduce() {
std::cout << "Hi, I'm " << name << ". I'm " << age << " years old." << std::endl;
}
};
int main() {
// create an object of 'Person'
Person person;
person.name = "Alice";
person.age = 20;
person.introduce(); // call the function
return 0;
}
在这个例子中,我们为Person
类添加了一个构造函数和一个析构函数。当创建一个Person
的对象时,构造函数会自动被调用,当对象的生命周期结束时,析构函数会自动被调用。
5.2.3 练习题
练习1:编写一个C++程序,定义一个类,这个类包含一个构造函数和一个析构函数,然后创建一个这个类的对象,观察构造函数和析构函数的调用顺序。
练习2:编写一个C++程序,定义一个类,这个类包含一个参数化的构造函数,并在构造函数中初始化对象的数据。
5.2.4 项目实践
项目1:创建一个C++项目,例如一个简单的银行账户管理系统。在这个系统中,你可以定义一个Account
类,这个类包含一些数据,如账户名、账户余额等,以及一些函数,如存款、取款、查询余额等。这个类也可以包含一个构造函数,用于初始化账户名和账户余额,以及一个析构函数,用于打印一条账户已关闭的消息。
5.3 访问控制(公有、私有、保护)
5.3.1 理论讲解
C++支持三种不同的访问修饰符:公有(public)、私有(private)和保护(protected)。
- 公有(public)成员在程序的任何地方都可以访问。
- 私有(private)成员只能在类的内部访问,也就是只能由类的成员函数访问。
- 保护(protected)成员和私有成员类似,但是它在派生类中可以被访问。
默认情况下,类的所有成员都是私有的。
5.3.2 示例代码
以下是一个简单的C++程序,演示了如何使用公有、私有和保护成员:
#include <iostream>
#include <string>
// define a class named 'Person'
class Person {
public:
// public data
std::string name;
private:
// private data
int age;
public:
// public function
void setAge(int a) {
age = a;
}
// public function
void introduce() {
std::cout << "Hi, I'm " << name << ". I'm " << age << " years old." << std::endl;
}
};
int main() {
// create an object of 'Person'
Person person;
person.name = "Alice";
person.setAge(20);
person.introduce(); // call the function
return 0;
}
在这个例子中,name
是公有的,可以在类的外部直接访问;age
是私有的,只能在类的内部访问,所以我们提供了一个公有的成员函数setAge
来修改age
的值。
5.3.3 练习题
练习1:编写一个C++程序,定义一个类,这个类包含公有、私有和保护成员,然后创建一个这个类的对象,尝试访问这些成员。
练习2:编写一个C++程序,定义一个类,这个类包含私有的数据和公有的成员函数,通过这些公有的成员函数访问和修改私有的数据。
5.3.4 项目实践
项目1:创建一个C++项目,例如一个简单的图书信息管理系统。在这个系统中,你可以定义一个Book
类,这个类包含私有的数据,如书名、作者、价格等,以及公有的成员函数,如设置书名、设置作者、设置价格、打印书籍信息等。
5.4 继承
5.4.1 理论讲解
继承是面向对象编程的一个重要特性,它允许我们创建一个新的类,继承并扩展已有类的功能。在C++中,新的类被称为子类或派生类,已有的类被称为父类或基类。
通过继承,子类继承了父类的所有公有和保护成员(私有成员不被继承)。这样,子类就可以使用父类的功能,而不需要重新编写相同的代码。
5.4.2 示例代码
以下是一个简单的C++程序,演示了如何使用继承:
#include <iostream>
#include <string>
// define a base class named 'Person'
class Person {
public:
std::string name;
int age;
void introduce() {
std::cout << "Hi, I'm " << name << ". I'm " << age << " years old." << std::endl;
}
};
// define a derived class named 'Student'
class Student : public Person {
public:
std::string school;
void study() {
std::cout << "I'm studying at " << school << "." << std::endl;
}
};
int main() {
// create an object of 'Student'
Student student;
student.name = "Alice";
student.age = 20;
student.school = "XYZ University";
student.introduce(); // call the function of the base class
student.study(); // call the function of the derived class
return 0;
}
在这个例子中,Student
类继承了Person
类,所以Student
类的对象可以使用Person
类的name
、age
和introduce
成员。
5.4.3 练习题
练习1:编写一个C++程序,定义一个基类和一个派生类,然后创建一个派生类的对象,通过这个对象访问基类和派生类的成员。
练习2:编写一个C++程序,定义一个基类和两个派生类,观察两个派生类是否可以访问基类的公有和保护成员。
5.4.4 项目实践
项目1:创建一个C++项目,例如一个简单的动物园管理系统。在这个系统中,你可以定义一个Animal
基类,这个类包含一些公有的数据,如名字、年龄等,以及一些公有的函数,如吃、睡等。然后,你可以定义一些派生类,如Lion
、Elephant
、Monkey
等,这些类继承了Animal
类,并添加了一些新的数据和函数。
5.5 多态
5.5.1 理论讲解
多态是面向对象编程的一个重要特性,它允许我们以统一的方式处理不同类型的对象。在C++中,我们主要通过虚函数(virtual function)来实现多态。
虚函数是在基类中声明的,并在派生类中重新定义的成员函数。通过使用虚函数,我们可以实现运行时多态,即在运行时决定调用哪个类的成员函数。
5.5.2 示例代码
以下是一个简单的C++程序,演示了如何使用虚函数来实现多态:
#include <iostream>
#include <string>
// define a base class named 'Animal'
class Animal {
public:
virtual void makeSound() {
std::cout << "The animal makes a sound." << std::endl;
}
};
// define a derived class named 'Dog'
class Dog : public Animal {
public:
void makeSound() override {
std::cout << "The dog barks." << std::endl;
}
};
// define another derived class named 'Cat'
class Cat : public Animal {
public:
void makeSound() override {
std::cout << "The cat meows." << std::endl;
}
};
void playSound(Animal& animal) {
animal.makeSound();
}
int main() {
Dog dog;
Cat cat;
playSound(dog); // The dog barks.
playSound(cat); // The cat meows.
return 0;
}
在这个例子中,Animal
类定义了一个虚函数makeSound
,Dog
类和Cat
类都重新定义了这个函数。在playSound
函数中,我们可以使用一个Animal
类型的引用来调用makeSound
函数,实际调用的是哪个类的函数,取决于运行时传入的实际对象。
5.5.3 练习题
练习1:编写一个C++程序,定义一个基类和一个派生类,基类中包含一个虚函数,派生类中重写这个虚函数,然后通过基类的指针或引用调用这个函数,观察运行结果。
练习2:编写一个C++程序,定义一个基类和两个派生类,每个类中都有一个虚函数,然后通过基类的指针或引用分别调用这个函数,观察运行结果。
5.5.4 项目实践
项目1:创建一个C++项目,例如一个简单的图形绘制程序。在这个程序中,你可以定义一个Shape
基类,这个类包含一个虚函数draw
。然后,你可以定义一些派生类,如Circle
、Rectangle
、Triangle
等,这些类都重写了draw
函数。最后,你可以写一个函数,这个函数接受一个Shape
类型的引用或指针,然后调用draw函数,观察运行结果。
C++高级主题
6.1 异常处理
6.1.1 理论讲解
异常是程序在执行期间发生的问题。C++提供了异常处理机制来捕获和处理异常,防止程序崩溃。异常处理的主要关键字有try
、catch
和throw
。
try
:一个可能抛出异常的代码块。
catch
:捕获异常并定义如何处理它。
throw
:当问题出现时,程序会抛出一个异常。
6.1.2 示例代码
以下是一个简单的C++程序,演示了如何使用异常处理:
#include <iostream>
int divide(int num, int den) {
if (den == 0) {
throw "Division by zero is not allowed.";
}
return num / den;
}
int main() {
try {
std::cout << divide(10, 2) << std::endl; // Output: 5
std::cout << divide(10, 0) << std::endl; // Throws an exception
} catch (const char* msg) {
std::cerr << "Error: " << msg << std::endl;
}
return 0;
}
在这个例子中,divide
函数在除数为零时抛出一个异常。main
函数中的try
块包含了可能抛出异常的代码,catch
块则捕获并处理异常。
6.1.3 练习题
练习1:编写一个C++程序,定义一个函数,这个函数在接收到非法参数时抛出一个异常,然后在主函数中捕获并处理这个异常。
练习2:编写一个C++程序,定义一个函数,这个函数在接收到非法参数时抛出一个异常,然后在另一个函数中调用这个函数,并在这个函数中捕获并处理异常。
6.1.4 项目实践
项目1:创建一个C++项目,例如一个简单的图书管理系统。在这个系统中,你可以定义一个函数,这个函数接收一个书籍的ID,然后返回这个书籍的信息。如果书籍ID不存在,这个函数就抛出一个异常。在主函数中,你可以捕获并处理这个异常。
6.2 模板
6.2.1 理论讲解
模板是C++中的一种特性,它允许我们定义通用的类型无关的代码。C++中的模板主要有两种:函数模板和类模板。
函数模板是一种函数,它的类型(如参数类型和返回类型)可以被参数化。通过函数模板,我们可以避免为不同类型的参数编写多个重载函数。
类模板则是一种类,它的类型(如成员函数的参数类型和返回类型,成员变量的类型)可以被参数化。通过类模板,我们可以避免为不同类型的成员编写多个类。
6.2.2 示例代码
以下是一些C++程序,演示了如何使用函数模板和类模板:
函数模板:
#include <iostream>
// function template
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << add<int>(10, 20) << std::endl; // Output: 30
std::cout << add<double>(1.5, 2.3) << std::endl; // Output: 3.8
return 0;
}
类模板:
#include <iostream>
// class template
template <typename T>
class Box {
public:
Box(T value) : value(value) {}
void print() const {
std::cout << "Box: " << value << std::endl;
}
private:
T value;
};
int main() {
Box<int> intBox(10);
intBox.print(); // Output: Box: 10
Box<std::string> stringBox("hello");
stringBox.print(); // Output: Box: hello
return 0;
}
6.2.3 练习题
练习1:编写一个C++程序,定义一个函数模板,这个函数模板接收两个参数,并返回它们的最大值。
练习2:编写一个C++程序,定义一个类模板,这个类模板包含一个成员变量和一个成员函数,这个成员函数可以修改成员变量的值。
6.2.4 项目实践
项目1:创建一个C++项目,例如一个简单的数据结构库。在这个库中,你可以定义一些数据结构的类模板,如链表、栈、队列等。这些类模板包含一些成员函数,如添加元素、删除元素、查找元素等。
6.3 命名空间
6.3.1 理论讲解
在C++中,命名空间是用于组织代码以防止命名冲突的特性。命名空间可以嵌套,并且可以跨越多个文件。在命名空间内部,可以定义函数、变量、类等。
std
是C++标准库中最常用的命名空间,包含了如cout
、cin
、string
等很多标准类型和函数。当我们使用这些类型和函数时,通常需要在前面添加std::
前缀,或者在文件开头添加using namespace std;
声明。
6.3.2 示例代码
以下是一个C++程序,演示了如何定义和使用命名空间:
#include <iostream>
// define a namespace
namespace my_namespace {
int my_var = 10;
void my_func() {
std::cout << "Hello from my_namespace!" << std::endl;
}
}
int main() {
// access the variable and function in the namespace
std::cout << my_namespace::my_var << std::endl; // Output: 10
my_namespace::my_func(); // Output: Hello from my_namespace!
return 0;
}
6.3.3 练习题
练习1:编写一个C++程序,定义两个包含相同名称的变量或函数的命名空间,然后在主函数中分别访问它们。
练习2:编写一个C++程序,定义一个命名空间,这个命名空间包含一个类的定义和一个全局变量的定义,然后在主函数中创建这个类的对象,并访问这个全局变量。
6.3.4 项目实践
项目1:创建一个C++项目,例如一个简单的数学库。在这个库中,你可以定义一个命名空间,这个命名空间包含一些数学函数的定义,如求和、求平均值、求标准差等。然后在主函数中,你可以调用这些函数进行计算。
6.4 文件和流
6.4.1 理论讲解
C++ 中文件和流操作是一种关键的技术,它允许程序从文件或其他外部来源(比如网络)读取数据,或向文件或其他外部目标(比如网络)写入数据。C++标准库中的iostream库提供了一系列的工具来执行这些操作。
6.4.2 示例代码
以下是一个C++程序,演示了如何使用文件流进行文件读写操作:
文件写操作:
#include <fstream>
int main() {
std::ofstream outfile("test.txt");
if (outfile.is_open()) {
outfile << "Hello, World!" << std::endl;
outfile.close();
} else {
std::cout << "Unable to open file for writing." << std::endl;
}
return 0;
}
文件读操作:
#include <iostream>
#include <fstream>
#include <string>
int main() {
std::ifstream infile("test.txt");
std::string line;
if (infile.is_open()) {
while (getline(infile, line)) {
std::cout << line << std::endl;
}
infile.close();
} else {
std::cout << "Unable to open file for reading." << std::endl;
}
return 0;
}
6.4.3 练习题
练习1:编写一个C++程序,将一组数据写入文件,并读取该文件。
练习2:编写一个C++程序,从文件读取一段文本,统计并输出文本中的单词数量。
6.4.4 项目实践
项目1:创建一个C++项目,例如一个简单的日志系统。系统中,你需要定义一个日志类,该类提供一个接口用于添加日志条目,并将所有的日志条目写入到一个文件中。然后在程序的其他部分,你可以创建这个日志类的对象,并使用它来记录程序的运行情况。
6.5 STL(标准模板库)
6.5.1 理论讲解
标准模板库(Standard Template Library, STL)是C++标准库的一部分,它包含了一些通用的模板类和函数,例如向量(vector)、列表(list)、队列(queue)、栈(stack)、集合(set)、映射(map)等等。
STL不仅提供了这些数据结构,还提供了一些操作这些数据结构的算法,例如查找(find)、排序(sort)、反转(reverse)等等。
6.5.2 示例代码
以下是一个C++程序,演示了如何使用STL中的vector和sort函数:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
// create a vector
std::vector<int> vec = {3, 1, 4, 1, 5, 9};
// sort the vector
std::sort(vec.begin(), vec.end());
// print the sorted vector
for (int num : vec) {
std::cout << num << " ";
}
std::cout << std::endl; // Output: 1 1 3 4 5 9
return 0;
}
6.5.3 练习题
练习1:编写一个C++程序,使用STL中的list存储一组数据,并使用STL的算法反转这个列表。
练习2:编写一个C++程序,使用STL中的map存储一组单词及其出现的次数,然后打印出每个单词及其出现的次数。
6.5.4 项目实践
项目1:创建一个C++项目,例如一个简单的文本处理程序。在这个程序中,你可以使用STL中的string类来处理文本,使用vector或list来存储文本中的单词,使用map来统计单词的出现次数,然后使用STL的算法对单词进行排序或查找等操作。