第1节:开发环境的搭建与配置
1.1 目标
在本节课中,学生将学习如何在Windows上搭建一个现代化的C++开发环境,并使用VSCode和CMake工具进行C++程序的开发与调试。学生将掌握以下内容:
- 安装VSCode及C++插件
- 安装MinGW或其他C++编译器
- 安装并配置CMake
- 创建并编译第一个C++项目
- 使用VSCode调试C++程序
1.2 工具准备
- VSCode:一个开源的跨平台文本编辑器,具有强大的插件支持和内置调试功能。
- MinGW 或 其他C++编译器:提供在Windows下的GCC编译工具链。
- CMake:一个跨平台的构建工具,可以生成适用于不同平台的构建文件。
步骤1:安装VSCode
- 打开VSCode官方网站,下载适用于Windows的安装包。
- 按照提示完成安装,选择默认设置。
- 安装完成后,启动VSCode。
步骤2:安装C++插件
- 在VSCode主界面的左侧扩展图标点击,打开扩展市场。
- 搜索
C++
,找到C/C++插件(由Microsoft发布),点击安装。 - 安装完成后,VSCode会提示重启编辑器,按照提示操作。
步骤3:安装MinGW或其他编译器
- 打开MinGW官方网站,下载并安装MinGW。
- 安装过程中,确保选中
g++
编译器组件。 - 安装完成后,配置系统环境变量:
- 右键“此电脑”,选择“属性” -> “高级系统设置” -> “环境变量”。
- 在“系统变量”中,找到并编辑
Path
变量,添加MinGW的bin
路径(例如C:\MinGW\bin
)。
- 打开命令提示符,输入
g++ --version
验证安装是否成功。
步骤4:安装CMake
- 打开CMake官方网站,下载适用于Windows的安装包。
- 按照默认选项进行安装,并确保在安装过程中选中了"Add CMake to the system PATH"选项,以便CMake命令行工具能够全局访问。
- 安装完成后,打开命令提示符,输入
cmake --version
,检查是否安装成功。
步骤5:配置VSCode的C++开发环境
- 启动VSCode,按
Ctrl+Shift+P
打开命令面板,输入CMake: Quick Start
,并按回车。 - 选择一个文件夹作为项目目录。
- 输入项目名称(例如
HelloWorld
)。 - 选择
C++
作为项目语言,继续选择最适合的编译器工具链。 - VSCode会自动生成一个基本的CMake项目结构,包括
CMakeLists.txt
文件。
步骤6:编写第一个C++程序
- 在
src
文件夹中,创建一个名为main.cpp
的文件,输入以下代码:#include <iostream> int main() { std::cout << "Hello, World!" << std::endl; return 0; }
- 保存文件。
步骤7:编译与运行程序
- 在VSCode中,点击左侧活动栏中的CMake Tools图标,选择
Build
,CMake会生成构建文件并编译项目。 - 当编译完成后,点击
Run
运行生成的可执行文件,你将会在VSCode的终端窗口中看到输出结果:Hello, World!
步骤8:调试C++程序
- 点击左侧活动栏中的调试图标,点击“创建launch.json文件”并选择C++调试器。
- 在
launch.json
文件中,VSCode会自动生成适合C++项目的调试配置。 - 设置断点(在代码行号处点击)。
- 点击开始调试,程序将会在断点处暂停,允许你检查变量和执行流。
1.3 小结
通过本节课的学习,学生已经成功搭建了一个现代C++开发环境,并使用VSCode、CMake和MinGW编译、运行了第一个C++程序。同时,学生也初步掌握了如何在VSCode中进行调试操作。
第2节:C++基础语法与结构
2.1 目标
本节课将引导学生了解C++的基础语法,包括程序的基本结构、数据类型、变量、输入输出以及条件语句的使用。通过本节课的学习,学生将能够编写简单的C++程序,并理解C++程序的运行方式。
2.2 基本概念
2.2.1 C++程序的基本结构
C++程序的基本结构通常包含以下部分:
- 预处理指令:如
#include <iostream>
,用于引入标准库。 - 主函数:C++程序从
main()
函数开始执行。 - 语句:每个语句以分号结尾。
示例代码:
#include <iostream>
int main() {
std::cout << "Welcome to C++!" << std::endl;
return 0;
}
2.2.2 数据类型、变量与常量
- 数据类型:C++提供了多种基本数据类型,如
int
、float
、double
、char
、bool
等。 - 变量:用来存储不同类型的数据,必须在使用前声明。
- 常量:使用
const
关键字声明,其值不可更改。
示例代码:
int main() {
int age = 20;
float height = 1.75;
const int year = 2024;
std::cout << "Age: " << age << ", Height: " << height << ", Year: " << year << std::endl;
return 0;
}
2.2.3 输入与输出
std::cout
用于输出信息到控制台。std::cin
用于从控制台输入数据。
示例代码:
#include <iostream>
int main() {
int age;
std::cout << "Enter your age: ";
std::cin >> age;
std::cout << "You are " << age << " years old." << std::endl;
return 0;
}
2.2.4 基本运算符与表达式
- 算术运算符:
+
、-
、*
、/
、%
。 - 比较运算符:
==
、!=
、>
、<
、>=
、<=
。 - 逻辑运算符:
&&
、||
、!
。
示例代码:
#include <iostream>
int main() {
int a = 5, b = 3;
std::cout << "Sum: " << (a + b) << std::endl;
std::cout << "Comparison (a > b): " << (a > b) << std::endl;
return 0;
}
2.2.5 条件语句
C++中的条件语句用于根据条件的真假执行不同的代码。常用的条件语句有if
、else if
、else
以及switch
。
- if-else语句:根据布尔表达式的值决定执行哪段代码。
- switch语句:用于对一个表达式的值进行多重判断。
示例代码:
#include <iostream>
int main() {
int score;
std::cout << "Enter your score: ";
std::cin >> score;
if (score >= 90) {
std::cout << "Grade: A" << std::endl;
} else if (score >= 80) {
std::cout << "Grade: B" << std::endl;
} else if (score >= 70) {
std::cout << "Grade: C" << std::endl;
} else {
std::cout << "Grade: F" << std::endl;
}
return 0;
}
2.3 实验任务:编写一个简单的计算器
在本节的实验任务中,学生将编写一个简单的控制台计算器,用户可以输入两个整数和一个运算符,程序将计算并输出结果。
实验步骤:
- 创建一个新的C++文件
calculator.cpp
。 - 实现以下功能:
- 用户输入两个整数和一个运算符(
+
、-
、*
、/
)。 - 程序根据用户输入的运算符执行相应的运算,并输出结果。
- 如果输入无效,程序应提示用户重新输入。
- 用户输入两个整数和一个运算符(
示例代码:
#include <iostream>
int main() {
int num1, num2;
char op;
std::cout << "Enter first number: ";
std::cin >> num1;
std::cout << "Enter operator (+, -, *, /): ";
std::cin >> op;
std::cout << "Enter second number: ";
std::cin >> num2;
switch (op) {
case '+':
std::cout << "Result: " << (num1 + num2) << std::endl;
break;
case '-':
std::cout << "Result: " << (num1 - num2) << std::endl;
break;
case '*':
std::cout << "Result: " << (num1 * num2) << std::endl;
break;
case '/':
if (num2 != 0) {
std::cout << "Result: " << (num1 / num2) << std::endl;
} else {
std::cout << "Error: Division by zero!" << std::endl;
}
break;
default:
std::cout << "Invalid operator!" << std::endl;
}
return 0;
}
2.4 小结
本节课通过基础的C++语法讲解,帮助学生了解了如何使用变量、运算符、输入输出以及条件语句来编写简单的C++程序。通过实验任务,学生学会了结合多个C++基础语法,开发一个简单的控制台应用程序。
第3节:循环与数组
3.1 目标
本节课将学习C++中的循环结构和数组的基本用法。学生将了解如何通过循环实现重复操作,并使用数组存储和处理多个数据。通过本节的实验任务,学生将应用这些概念实现简单的算法。
3.2 基本概念
3.2.1 循环结构
C++支持三种主要的循环结构:for
循环、while
循环、do-while
循环。
-
for循环:通常用于已知循环次数的情况。
语法:
for (初始化; 条件; 更新) { // 循环体 }
示例代码:
for (int i = 0; i < 5; i++) { std::cout << "Iteration " << i << std::endl; }
-
while循环:用于在条件为真时反复执行的场合,通常适用于条件未知或动态变化的情况。
语法:
while (条件) { // 循环体 }
示例代码:
int i = 0; while (i < 5) { std::cout << "Iteration " << i << std::endl; i++; }
-
do-while循环:与
while
循环类似,但保证循环体至少执行一次。语法:
do { // 循环体 } while (条件);
示例代码:
int i = 0; do { std::cout << "Iteration " << i << std::endl; i++; } while (i < 5);
3.2.2 数组
-
数组:用于存储相同类型的多个数据。数组的大小在声明时确定,并且数组的元素通过下标进行访问(从0开始)。
语法:
数据类型 数组名[大小];
示例代码:
int numbers[5] = {10, 20, 30, 40, 50}; for (int i = 0; i < 5; i++) { std::cout << "Element at index " << i << " is " << numbers[i] << std::endl; }
3.2.3 多维数组
-
多维数组:用于表示二维或更高维度的数据,例如矩阵。
语法:
数据类型 数组名[行][列];
示例代码:
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}}; for (int i = 0; i < 2; i++) { for (int j = 0; j < 3; j++) { std::cout << matrix[i][j] << " "; } std::cout << std::endl; }
3.3 基础算法练习
3.3.1 查找
-
线性查找:遍历数组查找特定元素,返回其下标。
示例代码:
int numbers[] = {10, 20, 30, 40, 50}; int target = 30; int index = -1; for (int i = 0; i < 5; i++) { if (numbers[i] == target) { index = i; break; } } if (index != -1) { std::cout << "Element found at index " << index << std::endl; } else { std::cout << "Element not found" << std::endl; }
3.3.2 排序(冒泡排序)
-
冒泡排序:一种简单的排序算法,重复遍历数组并交换相邻的元素,直到整个数组有序。
示例代码:
int numbers[] = {50, 30, 20, 40, 10}; int n = 5; for (int i = 0; i < n-1; i++) { for (int j = 0; j < n-i-1; j++) { if (numbers[j] > numbers[j+1]) { // 交换 int temp = numbers[j]; numbers[j] = numbers[j+1]; numbers[j+1] = temp; } } } std::cout << "Sorted array: "; for (int i = 0; i < n; i++) { std::cout << numbers[i] << " "; } std::cout << std::endl;
3.4 实验任务:学生成绩管理程序
本节实验任务是编写一个简单的学生成绩管理程序,允许用户输入多个学生的成绩,并根据成绩进行统计和排序。要求学生应用循环、数组和基础算法。
实验步骤:
- 创建一个新的C++文件
grades.cpp
。 - 程序要求:
- 用户输入学生的数量,然后输入每个学生的成绩。
- 计算成绩的平均值。
- 输出最高分和最低分。
- 使用冒泡排序对成绩从高到低排序,并输出排序结果。
示例代码:
#include <iostream>
int main() {
int num_students;
std::cout << "Enter the number of students: ";
std::cin >> num_students;
int grades[num_students];
std::cout << "Enter the grades:" << std::endl;
for (int i = 0; i < num_students; i++) {
std::cin >> grades[i];
}
// 计算平均值
int sum = 0;
for (int i = 0; i < num_students; i++) {
sum += grades[i];
}
double average = static_cast<double>(sum) / num_students;
std::cout << "Average grade: " << average << std::endl;
// 查找最高分和最低分
int max_grade = grades[0];
int min_grade = grades[0];
for (int i = 1; i < num_students; i++) {
if (grades[i] > max_grade) {
max_grade = grades[i];
}
if (grades[i] < min_grade) {
min_grade = grades[i];
}
}
std::cout << "Highest grade: " << max_grade << std::endl;
std::cout << "Lowest grade: " << min_grade << std::endl;
// 冒泡排序
for (int i = 0; i < num_students-1; i++) {
for (int j = 0; j < num_students-i-1; j++) {
if (grades[j] < grades[j+1]) {
int temp = grades[j];
grades[j] = grades[j+1];
grades[j+1] = temp;
}
}
}
// 输出排序结果
std::cout << "Grades sorted from highest to lowest:" << std::endl;
for (int i = 0; i < num_students; i++) {
std::cout << grades[i] << " ";
}
std::cout << std::endl;
return 0;
}
3.5 小结
本节课通过讲解循环结构和数组的使用,帮助学生掌握了处理重复操作和存储多个数据的基础技能。同时,通过基础算法的应用,学生进一步理解了如何用C++编写有效的程序。在实验任务中,学生结合所学内容实现了一个简单的成绩管理程序。
第4节:函数与作用域
4.1 目标
本节课的重点是学习如何定义和使用函数。通过函数的学习,学生将掌握如何将程序逻辑进行模块化,提高代码的可读性和复用性。此外,学生还会了解变量的作用域和生命周期。
4.2 基本概念
4.2.1 函数的定义与调用
函数是一个包含特定任务的代码块,可以通过调用来执行函数中的代码。函数的定义通常包括返回类型、函数名、参数列表和函数体。
语法:
返回类型 函数名(参数类型 参数名, ...) {
// 函数体
return 返回值;
}
示例代码:
#include <iostream>
// 函数定义
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3); // 函数调用
std::cout << "Sum: " << result << std::endl;
return 0;
}
4.2.2 函数的参数传递
- 值传递:函数接收到的是参数的副本,对副本的修改不会影响原始变量。
- 引用传递:通过引用传递参数,函数可以直接修改原始变量的值。
示例代码(引用传递):
#include <iostream>
// 函数定义,使用引用传递
void swap(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 5, y = 10;
std::cout << "Before swap: x = " << x << ", y = " << y << std::endl;
swap(x, y);
std::cout << "After swap: x = " << x << ", y = " << y << std::endl;
return 0;
}
4.2.3 函数的返回值
函数可以返回任意数据类型,也可以通过void
定义不返回任何值的函数。
示例代码(返回值):
#include <iostream>
// 返回最大值的函数
int max(int a, int b) {
return (a > b) ? a : b;
}
int main() {
int result = max(10, 20);
std::cout << "Max: " << result << std::endl;
return 0;
}
4.2.4 函数重载
函数重载允许多个同名函数存在,只要它们的参数类型或数量不同即可。编译器会根据函数调用时的参数类型来确定调用哪个重载函数。
示例代码:
#include <iostream>
// 重载函数
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int main() {
std::cout << "Int sum: " << add(3, 4) << std::endl;
std::cout << "Double sum: " << add(3.5, 4.5) << std::endl;
return 0;
}
4.2.5 递归函数
递归函数是指在函数的定义中调用该函数本身。递归在处理某些问题时非常有用,例如计算阶乘或解决分治问题。
示例代码(递归计算阶乘):
#include <iostream>
// 递归函数定义
int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
int main() {
int result = factorial(5);
std::cout << "Factorial of 5: " << result << std::endl;
return 0;
}
4.2.6 变量的作用域与生命周期
- 局部变量:声明在函数内部的变量,其作用域仅限于该函数。
- 全局变量:声明在所有函数之外的变量,作用域为整个程序。
- 静态变量:在函数中声明为
static
的变量,其生命周期在整个程序运行期间保持有效,但其作用域仍然是函数内部。
示例代码(局部与全局变量):
#include <iostream>
int globalVar = 10; // 全局变量
void showVar() {
int localVar = 5; // 局部变量
std::cout << "Local variable: " << localVar << std::endl;
std::cout << "Global variable: " << globalVar << std::endl;
}
int main() {
showVar();
std::cout << "Global variable in main: " << globalVar << std::endl;
return 0;
}
4.3 函数与模块化编程
函数是模块化编程的重要组成部分,它能够帮助开发者将复杂的程序拆分为多个独立的功能模块。通过合理的函数划分,代码的可读性和复用性将大大提高。
4.4 实验任务:编写一个计算几何图形面积的程序
本节的实验任务是编写一个程序,计算矩形、三角形和圆形的面积。要求学生使用函数实现每种图形的面积计算,并通过函数调用来获取不同图形的面积。
实验步骤:
- 创建一个新的C++文件
geometry.cpp
。 - 实现以下功能:
- 编写三个函数,分别计算矩形、三角形和圆形的面积。
- 用户可以选择计算哪种图形的面积。
- 根据用户输入的图形参数,调用相应的函数并输出结果。
示例代码:
#include <iostream>
#include <cmath>
// 计算矩形面积的函数
double rectangleArea(double length, double width) {
return length * width;
}
// 计算三角形面积的函数
double triangleArea(double base, double height) {
return 0.5 * base * height;
}
// 计算圆形面积的函数
double circleArea(double radius) {
return M_PI * radius * radius;
}
int main() {
int choice;
std::cout << "Choose a shape to calculate the area:" << std::endl;
std::cout << "1. Rectangle" << std::endl;
std::cout << "2. Triangle" << std::endl;
std::cout << "3. Circle" << std::endl;
std::cin >> choice;
switch (choice) {
case 1: {
double length, width;
std::cout << "Enter length and width: ";
std::cin >> length >> width;
std::cout << "Rectangle area: " << rectangleArea(length, width) << std::endl;
break;
}
case 2: {
double base, height;
std::cout << "Enter base and height: ";
std::cin >> base >> height;
std::cout << "Triangle area: " << triangleArea(base, height) << std::endl;
break;
}
case 3: {
double radius;
std::cout << "Enter radius: ";
std::cin >> radius;
std::cout << "Circle area: " << circleArea(radius) << std::endl;
break;
}
default:
std::cout << "Invalid choice!" << std::endl;
}
return 0;
}
4.5 小结
本节课通过学习函数的定义、调用、参数传递、递归与重载等概念,学生能够编写结构化的C++程序。结合变量的作用域和生命周期的知识,学生可以更好地理解程序的运行过程。本节的实验任务通过计算几何图形面积,帮助学生加深了对函数的实际应用。
第5节:指针与动态内存管理
5.1 目标
本节课将深入学习C++中的指针与动态内存管理。学生将了解指针的概念及其与内存地址的关系,学习如何通过指针访问和操作内存。此外,还会介绍如何使用new
和delete
动态管理内存,以便在程序中灵活处理内存需求。
5.2 基本概念
5.2.1 指针的概念
指针是保存内存地址的变量。通过指针,程序可以直接访问和修改存储在该地址上的数据。
语法:
数据类型* 指针名;
示例代码:
#include <iostream>
int main() {
int a = 10;
int* ptr = &a; // 指针保存变量a的地址
std::cout << "Value of a: " << a << std::endl;
std::cout << "Address of a: " << &a << std::endl;
std::cout << "Pointer value (address of a): " << ptr << std::endl;
std::cout << "Dereferenced pointer (value of a): " << *ptr << std::endl;
return 0;
}
5.2.2 指针的基本操作
- 取地址运算符
&
:用于获取变量的内存地址。 - 解引用运算符
*
:用于通过指针访问存储在该地址上的数据。
示例代码:
#include <iostream>
int main() {
int x = 42;
int* ptr = &x; // 获取x的地址
std::cout << "Value of x: " << x << std::endl;
*ptr = 100; // 通过指针修改x的值
std::cout << "New value of x: " << x << std::endl;
return 0;
}
5.2.3 指针与数组
在C++中,数组名本身就是一个指向数组第一个元素的指针。通过指针可以遍历数组。
示例代码:
#include <iostream>
int main() {
int arr[3] = {10, 20, 30};
int* ptr = arr; // 数组名即指向第一个元素的指针
for (int i = 0; i < 3; i++) {
std::cout << "arr[" << i << "] = " << *(ptr + i) << std::endl; // 通过指针访问数组元素
}
return 0;
}
5.2.4 动态内存分配
new
运算符:用于动态分配内存,返回指向分配内存的指针。delete
运算符:用于释放动态分配的内存,防止内存泄漏。
示例代码(动态分配单个变量):
#include <iostream>
int main() {
int* ptr = new int; // 动态分配一个整数
*ptr = 42;
std::cout << "Dynamically allocated value: " << *ptr << std::endl;
delete ptr; // 释放动态分配的内存
return 0;
}
示例代码(动态分配数组):
#include <iostream>
int main() {
int* arr = new int[5]; // 动态分配一个数组
for (int i = 0; i < 5; i++) {
arr[i] = i * 10;
}
std::cout << "Dynamically allocated array:" << std::endl;
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
delete[] arr; // 释放动态分配的数组内存
return 0;
}
5.2.5 空指针与指针检查
- 空指针(
nullptr
):用于表示指针不指向任何有效内存地址。 - 指针检查:在使用指针之前,应确保指针不为空。
示例代码:
#include <iostream>
int main() {
int* ptr = nullptr; // 空指针
if (ptr == nullptr) {
std::cout << "Pointer is null." << std::endl;
}
return 0;
}
5.2.6 常见的指针错误
- 悬空指针:指向已被释放或无效的内存。
- 野指针:未被初始化的指针。
- 内存泄漏:动态分配的内存未被释放。
5.3 实验任务:学生成绩的动态存储
本节的实验任务是编写一个程序,允许用户动态输入学生成绩,并存储这些成绩以便后续处理。要求学生使用指针和动态内存管理实现。
实验步骤:
- 创建一个新的C++文件
dynamic_grades.cpp
。 - 实现以下功能:
- 用户输入学生人数,动态分配内存以存储学生的成绩。
- 用户输入每个学生的成绩。
- 计算并输出成绩的平均值。
- 释放动态分配的内存。
示例代码:
#include <iostream>
int main() {
int num_students;
std::cout << "Enter the number of students: ";
std::cin >> num_students;
// 动态分配用于存储学生成绩的内存
int* grades = new int[num_students];
// 输入每个学生的成绩
std::cout << "Enter the grades:" << std::endl;
for (int i = 0; i < num_students; i++) {
std::cin >> grades[i];
}
// 计算平均成绩
int sum = 0;
for (int i = 0; i < num_students; i++) {
sum += grades[i];
}
double average = static_cast<double>(sum) / num_students;
std::cout << "Average grade: " << average << std::endl;
// 释放动态分配的内存
delete[] grades;
return 0;
}
5.4 深入理解:指针与函数
指针可以作为函数的参数传递,用于在函数中修改实参的值或访问动态分配的内存。指针也可以用来返回动态分配的内存地址。
示例代码(指针作为参数):
#include <iostream>
// 使用指针交换两个整数的值
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 10, y = 20;
std::cout << "Before swap: x = " << x << ", y = " << y << std::endl;
swap(&x, &y); // 传递变量的地址
std::cout << "After swap: x = " << x << ", y = " << y << std::endl;
return 0;
}
5.5 小结
本节课通过指针和动态内存管理的学习,学生能够理解内存的操作原理,并掌握动态分配和释放内存的方法。通过实验任务,学生实践了如何灵活处理数据的存储与管理。掌握指针是C++编程的重要基础,在后续的C++高级编程中,指针的概念将会更加广泛地应用。
第6节:面向对象编程基础
6.1 目标
本节课将介绍C++中的面向对象编程(OOP)概念,帮助学生理解类与对象的基本原理。通过学习类的定义、对象的创建与使用、构造函数与析构函数、成员函数与成员变量等内容,学生将掌握如何使用OOP思想来设计和实现程序。
6.2 基本概念
6.2.1 类与对象
类是面向对象编程中的核心概念,它是数据和方法的封装体。对象是类的实例,具有类中定义的属性和行为。
类的定义语法:
class 类名 {
public:
// 公共成员变量和成员函数
数据类型 变量名;
返回类型 函数名(参数);
};
示例代码:
#include <iostream>
class Person {
public:
// 成员变量
std::string name;
int age;
// 成员函数
void introduce() {
std::cout << "Hi, my name is " << name << " and I am " << age << " years old." << std::endl;
}
};
int main() {
// 创建对象
Person person;
person.name = "Alice";
person.age = 25;
person.introduce();
return 0;
}
6.2.2 构造函数与析构函数
- 构造函数:用于在创建对象时初始化对象的状态。构造函数与类同名,没有返回类型。
- 析构函数:用于在对象销毁时执行清理工作,析构函数的名字在类名前加
~
,同样没有返回类型。
示例代码:
#include <iostream>
class Person {
public:
std::string name;
int age;
// 构造函数
Person(std::string n, int a) : name(n), age(a) {
std::cout << "Person object created!" << std::endl;
}
// 析构函数
~Person() {
std::cout << "Person object destroyed!" << std::endl;
}
void introduce() {
std::cout << "Hi, my name is " << name << " and I am " << age << " years old." << std::endl;
}
};
int main() {
// 使用构造函数创建对象
Person person("Bob", 30);
person.introduce(); // 输出介绍信息
return 0;
}
6.2.3 访问控制(public、private、protected)
- public:公共成员可以在类的外部访问。
- private:私有成员只能在类的内部访问。
- protected:受保护的成员只能在类内部或派生类中访问。
示例代码:
#include <iostream>
class Person {
private:
std::string name;
int age;
public:
// 公共成员函数用于访问私有成员
void setDetails(std::string n, int a) {
name = n;
age = a;
}
void introduce() {
std::cout << "Hi, my name is " << name << " and I am " << age << " years old." << std::endl;
}
};
int main() {
Person person;
person.setDetails("Charlie", 28);
person.introduce();
return 0;
}
6.2.4 成员函数与成员变量
成员函数是类内部定义的函数,用于操作成员变量。成员变量存储对象的状态,成员函数则定义对象的行为。
示例代码:
#include <iostream>
class Circle {
public:
double radius;
// 成员函数:计算圆的面积
double getArea() {
return 3.14159 * radius * radius;
}
};
int main() {
Circle circle;
circle.radius = 5.0;
std::cout << "Circle area: " << circle.getArea() << std::endl;
return 0;
}
6.3 深入理解类与对象
6.3.1 this指针
this
指针是类中隐含的指针,它指向当前对象,可以用来区分成员变量与函数参数重名的情况。
示例代码:
#include <iostream>
class Rectangle {
private:
int width, height;
public:
// 使用this指针来区分成员变量和参数
void setDimensions(int width, int height) {
this->width = width;
this->height = height;
}
int getArea() {
return width * height;
}
};
int main() {
Rectangle rect;
rect.setDimensions(5, 10);
std::cout << "Rectangle area: " << rect.getArea() << std::endl;
return 0;
}
6.3.2 静态成员变量与静态成员函数
静态成员变量属于类,而不属于某个特定对象。所有对象共享静态成员变量。静态成员函数可以直接访问静态成员变量。
示例代码:
#include <iostream>
class Counter {
public:
static int count;
Counter() {
count++;
}
static void showCount() {
std::cout << "Count: " << count << std::endl;
}
};
// 初始化静态成员变量
int Counter::count = 0;
int main() {
Counter c1, c2, c3;
Counter::showCount(); // 调用静态成员函数
return 0;
}
6.4 实验任务:学生信息管理系统
本节的实验任务是设计一个简单的学生信息管理系统,使用面向对象的方式存储和管理学生的信息。要求实现以下功能:
- 定义一个
Student
类,包含学生的姓名、年龄和成绩等属性。 - 提供构造函数和成员函数用于操作学生数据。
- 创建多个
Student
对象,并展示每个学生的信息。
实验步骤:
- 创建一个新的C++文件
student_system.cpp
。 - 实现以下功能:
- 定义
Student
类,包含姓名、年龄和成绩等私有成员。 - 编写构造函数和
introduce
函数,用于输出学生信息。 - 在
main
函数中创建并初始化多个学生对象,调用相关函数输出学生信息。
- 定义
示例代码:
#include <iostream>
class Student {
private:
std::string name;
int age;
double grade;
public:
// 构造函数
Student(std::string n, int a, double g) : name(n), age(a), grade(g) {}
// 输出学生信息
void introduce() {
std::cout << "Student: " << name << ", Age: " << age << ", Grade: " << grade << std::endl;
}
};
int main() {
// 创建学生对象
Student student1("Alice", 20, 88.5);
Student student2("Bob", 19, 91.2);
// 输出学生信息
student1.introduce();
student2.introduce();
return 0;
}
6.5 小结
本节课学生学习了面向对象编程的基础知识,包括类的定义、对象的创建、构造函数与析构函数、成员函数与成员变量、访问控制以及静态成员等概念。通过实验任务,学生加深了对OOP思想的理解,能够运用OOP方法进行程序设计。面向对象编程是C++中的核心思想,将在后续的课程中进一步扩展。
第7节:继承与多态
7.1 目标
本节课将介绍面向对象编程中的两个重要特性:继承与多态。继承允许一个类从另一个类继承属性和方法,复用代码并扩展功能;多态则使得不同类型的对象可以通过统一的接口进行操作,从而提高代码的灵活性。学生将学习如何定义基类和派生类,以及如何使用虚函数实现多态。
7.2 基本概念
7.2.1 继承
继承是从已有的类(基类或父类)创建新类(派生类或子类)的机制。派生类继承了基类的成员变量和成员函数,可以复用基类的代码并添加新的功能。
继承语法:
class 派生类名 : 访问修饰符 基类名 {
public:
// 派生类中的新成员变量和成员函数
};
示例代码:
#include <iostream>
class Animal {
public:
void eat() {
std::cout << "This animal is eating." << std::endl;
}
};
// Dog类继承自Animal类
class Dog : public Animal {
public:
void bark() {
std::cout << "The dog is barking." << std::endl;
}
};
int main() {
Dog dog;
dog.eat(); // 调用基类的函数
dog.bark(); // 调用派生类的函数
return 0;
}
7.2.2 访问修饰符在继承中的作用
继承时,可以通过访问修饰符控制基类成员在派生类中的可见性:
- public继承:基类的
public
成员在派生类中保持为public
,protected
成员保持为protected
,private
成员不可访问。 - protected继承:基类的
public
和protected
成员在派生类中变为protected
,private
成员不可访问。 - private继承:基类的所有非私有成员在派生类中变为
private
。
示例代码:
#include <iostream>
class Base {
public:
int x = 10;
protected:
int y = 20;
private:
int z = 30;
};
// 派生类继承自Base类
class Derived : public Base {
public:
void show() {
std::cout << "x = " << x << std::endl; // 可以访问public成员
std::cout << "y = " << y << std::endl; // 可以访问protected成员
// std::cout << "z = " << z << std::endl; // 无法访问private成员
}
};
int main() {
Derived d;
d.show();
return 0;
}
7.2.3 构造函数与析构函数在继承中的调用顺序
在继承结构中,基类的构造函数在派生类的构造函数之前调用,析构函数则按相反的顺序执行,即先调用派生类的析构函数,再调用基类的析构函数。
示例代码:
#include <iostream>
class Base {
public:
Base() {
std::cout << "Base constructor called." << std::endl;
}
~Base() {
std::cout << "Base destructor called." << std::endl;
}
};
class Derived : public Base {
public:
Derived() {
std::cout << "Derived constructor called." << std::endl;
}
~Derived() {
std::cout << "Derived destructor called." << std::endl;
}
};
int main() {
Derived d;
return 0;
}
7.3 多态
7.3.1 虚函数与动态绑定
多态允许使用基类的指针或引用操作派生类对象,从而实现灵活的运行时行为。要实现多态,必须使用虚函数(virtual function)。通过将基类中的函数声明为虚函数,可以确保派生类覆盖该函数时,在运行时调用的是派生类的实现,而不是基类的实现。
示例代码:
#include <iostream>
class Animal {
public:
// 基类中的虚函数
virtual void sound() {
std::cout << "This animal makes a sound." << std::endl;
}
};
class Dog : public Animal {
public:
// 覆盖基类的虚函数
void sound() override {
std::cout << "The dog barks." << std::endl;
}
};
class Cat : public Animal {
public:
// 覆盖基类的虚函数
void sound() override {
std::cout << "The cat meows." << std::endl;
}
};
int main() {
Animal* animal;
Dog dog;
Cat cat;
// 基类指针指向派生类对象
animal = &dog;
animal->sound(); // 调用Dog类的sound()函数
animal = &cat;
animal->sound(); // 调用Cat类的sound()函数
return 0;
}
7.3.2 纯虚函数与抽象类
如果基类中的某个虚函数没有具体实现,可以将其声明为纯虚函数,使得该类成为抽象类,不能实例化。派生类必须实现所有纯虚函数,才能创建对象。
语法:
virtual 返回类型 函数名(参数) = 0;
示例代码:
#include <iostream>
// 抽象类Shape
class Shape {
public:
// 纯虚函数
virtual void draw() = 0;
};
class Circle : public Shape {
public:
// 实现纯虚函数
void draw() override {
std::cout << "Drawing a circle." << std::endl;
}
};
class Square : public Shape {
public:
// 实现纯虚函数
void draw() override {
std::cout << "Drawing a square." << std::endl;
}
};
int main() {
Shape* shape;
Circle circle;
Square square;
shape = &circle;
shape->draw(); // 调用Circle的draw()函数
shape = □
shape->draw(); // 调用Square的draw()函数
return 0;
}
7.4 实验任务:多态的形状绘制系统
本节的实验任务是设计一个多态的形状绘制系统,要求定义一个抽象类Shape
,并由Circle
、Square
和Triangle
类继承。每个派生类实现draw()
函数,输出对应形状的绘制信息。要求使用多态机制,通过基类指针或引用操作派生类对象。
实验步骤:
- 创建一个新的C++文件
shape_system.cpp
。 - 实现以下功能:
- 定义
Shape
抽象类,包含纯虚函数draw()
。 - 由
Circle
、Square
、Triangle
类继承Shape
并实现draw()
函数。 - 在
main
函数中,创建一个Shape
指针数组,依次存放不同形状对象,通过多态调用draw()
函数。
- 定义
示例代码:
#include <iostream>
// 抽象类Shape
class Shape {
public:
virtual void draw() = 0; // 纯虚函数
};
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a circle." << std::endl;
}
};
class Square : public Shape {
public:
void draw() override {
std::cout << "Drawing a square." << std::endl;
}
};
class Triangle : public Shape {
public:
void draw() override {
std::cout << "Drawing a triangle." << std::endl;
}
};
int main() {
// 创建形状对象的指针数组
Shape* shapes[3];
shapes[0] = new Circle();
shapes[1] = new Square();
shapes[2] = new Triangle();
// 使用多态调用draw()函数
for (int i = 0; i < 3; i++) {
shapes[i]->draw();
}
// 释放内存
for (int i = 0; i < 3; i++) {
delete shapes[i];
}
return 0;
}
7.5 小结
本节课学生学习了继承和多态的概念与应用,继承帮助代码复用和扩展,虚函数与多态提供了灵活的运行时行为。通过实验任务,学生能够理解如何设计和实现支持多态的类层次结构,进一步掌握面向对象编程的核心思想。
第8节:智能指针与C++内存管理
8.1 目标
本节课的目标是帮助学生理解C++中的内存管理,包括动态内存分配和释放。为了更好地管理内存并避免内存泄漏,C++11引入了智能指针(smart pointers),如std::unique_ptr
、std::shared_ptr
和std::weak_ptr
。本节将学习如何使用这些智能指针来管理动态内存,并探讨它们的适用场景。
8.2 动态内存管理
8.2.1 动态内存分配与释放
在C++中,可以使用new
关键字动态分配内存,用delete
关键字释放内存。动态分配的内存不会自动释放,需要手动调用delete
,否则会产生内存泄漏。
示例代码:
#include <iostream>
int main() {
// 动态分配一个int变量
int* p = new int(10);
std::cout << "Value: " << *p << std::endl;
// 手动释放内存
delete p;
return 0;
}
8.2.2 动态分配数组
动态分配数组时,使用new[]
分配内存,使用delete[]
释放内存。
示例代码:
#include <iostream>
int main() {
// 动态分配一个数组
int* arr = new int[5];
for (int i = 0; i < 5; i++) {
arr[i] = i * 10;
}
for (int i = 0; i < 5; i++) {
std::cout << arr[i] << " ";
}
std::cout << std::endl;
// 释放数组内存
delete[] arr;
return 0;
}
8.3 智能指针
C++11引入了智能指针,用于自动管理动态内存,避免内存泄漏。智能指针是模板类,负责在对象不再使用时自动释放其占用的内存。
8.3.1 std::unique_ptr
std::unique_ptr
是一种独占型智能指针,确保同一时刻只有一个指针指向某个对象。不能复制unique_ptr
,只能通过转移所有权(使用std::move
)来共享对象的控制权。
使用场景:适合对象的独占所有权场景,如RAII(资源获取即初始化)模式。
示例代码:
#include <iostream>
#include <memory> // 包含智能指针库
class Resource {
public:
Resource() {
std::cout << "Resource acquired." << std::endl;
}
~Resource() {
std::cout << "Resource released." << std::endl;
}
};
int main() {
std::unique_ptr<Resource> res1 = std::make_unique<Resource>(); // 自动管理内存
// std::unique_ptr<Resource> res2 = res1; // 错误,不能复制unique_ptr
std::unique_ptr<Resource> res2 = std::move(res1); // 转移所有权
if (!res1) {
std::cout << "res1 no longer owns the resource." << std::endl;
}
return 0;
}
8.3.2 std::shared_ptr
std::shared_ptr
是一种共享所有权的智能指针,允许多个指针同时指向同一个对象。它使用引用计数来跟踪有多少指针指向该对象,当最后一个指针被销毁时,自动释放对象的内存。
使用场景:适合多个对象共享资源的场景,如缓存或共享数据。
示例代码:
#include <iostream>
#include <memory>
class Resource {
public:
Resource() {
std::cout << "Resource acquired." << std::endl;
}
~Resource() {
std::cout << "Resource released." << std::endl;
}
};
int main() {
std::shared_ptr<Resource> res1 = std::make_shared<Resource>();
std::cout << "Reference count: " << res1.use_count() << std::endl;
{
std::shared_ptr<Resource> res2 = res1; // 共享所有权
std::cout << "Reference count: " << res1.use_count() << std::endl;
} // res2销毁,但资源不会释放,因为res1还在使用
std::cout << "Reference count: " << res1.use_count() << std::endl;
return 0;
}
8.3.3 std::weak_ptr
std::weak_ptr
是一种弱引用,不增加对象的引用计数。它通常与std::shared_ptr
配合使用,解决循环引用的问题。weak_ptr
不能直接访问对象,需要先通过lock()
方法转换为shared_ptr
。
使用场景:适合防止循环引用的场景,如观察者模式或双向关联。
示例代码:
#include <iostream>
#include <memory>
class Resource {
public:
Resource() {
std::cout << "Resource acquired." << std::endl;
}
~Resource() {
std::cout << "Resource released." << std::endl;
}
};
int main() {
std::shared_ptr<Resource> res1 = std::make_shared<Resource>();
std::weak_ptr<Resource> weakRes = res1; // 创建一个弱引用
if (auto tempRes = weakRes.lock()) { // 检查对象是否仍然存在
std::cout << "Resource is still alive." << std::endl;
} else {
std::cout << "Resource has been released." << std::endl;
}
return 0;
}
8.4 实验任务:内存管理与智能指针
本节的实验任务是设计一个简单的内存管理系统,使用智能指针来管理动态分配的资源。任务要求包括:
- 使用
std::unique_ptr
管理独占资源。 - 使用
std::shared_ptr
管理共享资源,并显示引用计数。 - 使用
std::weak_ptr
避免循环引用。
实验步骤:
- 创建一个新的C++文件
smart_pointer_demo.cpp
。 - 实现以下功能:
- 使用
std::unique_ptr
创建独占资源并转移所有权。 - 使用
std::shared_ptr
管理共享资源,并在不同作用域中显示引用计数。 - 使用
std::weak_ptr
防止循环引用。
- 使用
示例代码:
#include <iostream>
#include <memory>
class Resource {
public:
Resource() {
std::cout << "Resource acquired." << std::endl;
}
~Resource() {
std::cout << "Resource released." << std::endl;
}
};
void uniquePointerDemo() {
std::unique_ptr<Resource> res1 = std::make_unique<Resource>();
std::unique_ptr<Resource> res2 = std::move(res1);
if (!res1) {
std::cout << "Unique pointer transferred ownership." << std::endl;
}
}
void sharedPointerDemo() {
std::shared_ptr<Resource> res1 = std::make_shared<Resource>();
std::cout << "Initial reference count: " << res1.use_count() << std::endl;
{
std::shared_ptr<Resource> res2 = res1;
std::cout << "Reference count in scope: " << res1.use_count() << std::endl;
}
std::cout << "Reference count after scope: " << res1.use_count() << std::endl;
}
void weakPointerDemo() {
std::shared_ptr<Resource> res1 = std::make_shared<Resource>();
std::weak_ptr<Resource> weakRes = res1;
if (auto tempRes = weakRes.lock()) {
std::cout << "Resource is still alive." << std::endl;
} else {
std::cout << "Resource has been released." << std::endl;
}
}
int main() {
std::cout << "Unique Pointer Demo:" << std::endl;
uniquePointerDemo();
std::cout << "\nShared Pointer Demo:" << std::endl;
sharedPointerDemo();
std::cout << "\nWeak Pointer Demo:" << std::endl;
weakPointerDemo();
return 0;
}
8.5 小结
本节课学生学习了C++中的内存管理,理解了动态内存分配和释放的原理,并掌握了如何使用C++11引入的智能指针来避免内存泄漏。通过智能指针的自动管理功能,学生能够编写出更健壮、更安全的代码。智能指针是现代C++中非常重要的工具,它们提高了代码的可维护性并减少了内存管理的复杂性。在后续课程中,智能指针将继续用于更复杂的项目开发。
第9节:模板与泛型编程
9.1 目标
本节课的目标是帮助学生理解C++模板的基本概念以及如何进行泛型编程。模板是C++的强大特性之一,能够让代码具有更高的复用性和灵活性。通过本节的学习,学生将学会编写函数模板、类模板,并了解模板特化和变参模板等高级用法。
9.2 函数模板
9.2.1 什么是函数模板?
函数模板是一种使函数能够处理不同数据类型的工具。通过使用模板,函数可以在编译时根据传入的参数类型自动生成对应的函数代码,避免为每种类型重复编写相同的逻辑。
语法:
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
示例代码:
#include <iostream>
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
int main() {
std::cout << "Max of 3 and 7: " << max(3, 7) << std::endl;
std::cout << "Max of 5.6 and 3.2: " << max(5.6, 3.2) << std::endl;
std::cout << "Max of 'a' and 'z': " << max('a', 'z') << std::endl;
return 0;
}
9.2.2 使用多个模板参数
函数模板可以使用多个模板参数,使函数能够处理多种类型的组合。
示例代码:
#include <iostream>
template<typename T1, typename T2>
auto add(T1 a, T2 b) {
return a + b;
}
int main() {
std::cout << "Addition of 5 and 3.2: " << add(5, 3.2) << std::endl;
return 0;
}
9.3 类模板
9.3.1 什么是类模板?
类模板允许定义模板类,从而创建可以处理不同数据类型的类。类模板在C++标准库(如std::vector
、std::map
等容器)中大量使用。
语法:
template<typename T>
class Box {
T value;
public:
Box(T val) : value(val) {}
T getValue() const { return value; }
};
示例代码:
#include <iostream>
template<typename T>
class Box {
T value;
public:
Box(T val) : value(val) {}
T getValue() const { return value; }
};
int main() {
Box<int> intBox(123);
Box<std::string> strBox("Hello, World!");
std::cout << "Integer Box: " << intBox.getValue() << std::endl;
std::cout << "String Box: " << strBox.getValue() << std::endl;
return 0;
}
9.3.2 类模板的部分特化
类模板的部分特化允许为某些特定类型或条件提供不同的实现。这在需要针对某些特殊类型优化或改变行为时非常有用。
示例代码:
#include <iostream>
template<typename T>
class Box {
T value;
public:
Box(T val) : value(val) {}
T getValue() const { return value; }
};
// 对指针类型进行部分特化
template<typename T>
class Box<T*> {
T* value;
public:
Box(T* val) : value(val) {}
T getValue() const { return *value; }
};
int main() {
int num = 42;
Box<int*> intPointerBox(&num);
std::cout << "Pointer Box: " << intPointerBox.getValue() << std::endl;
return 0;
}
9.4 变参模板
9.4.1 什么是变参模板?
变参模板(Variadic Templates)允许模板接收任意数量的参数。这使得编写更通用和灵活的模板成为可能,特别是在处理不定参数的情况下非常有用。
示例代码:
#include <iostream>
// 基础模板
template<typename T>
void print(T value) {
std::cout << value << std::endl;
}
// 递归展开模板参数
template<typename T, typename... Args>
void print(T value, Args... args) {
std::cout << value << ", ";
print(args...);
}
int main() {
print(1, 2.5, "Hello", 'A');
return 0;
}
9.4.2 结合变参模板和折叠表达式
C++17引入了折叠表达式(Fold Expression),使得对变参模板的参数执行操作变得更加简单。
示例代码:
#include <iostream>
// 使用折叠表达式计算参数之和
template<typename... Args>
auto sum(Args... args) {
return (args + ...);
}
int main() {
std::cout << "Sum: " << sum(1, 2, 3, 4, 5) << std::endl;
return 0;
}
9.5 实验任务:泛型编程
任务要求
- 编写一个函数模板
compare
,可以比较两个不同类型的值。 - 编写一个类模板
Pair
,能够存储一对不同类型的值,并实现方法first()
和second()
分别返回第一个和第二个值。 - 使用变参模板编写一个
printAll
函数,能够接受任意数量的参数并逐个打印。
实验步骤
- 创建一个新的C++文件
template_demo.cpp
。 - 实现函数模板
compare
,类模板Pair
,以及变参模板函数printAll
。 - 运行程序并测试这些模板函数。
示例代码:
#include <iostream>
// 1. 函数模板 compare
template<typename T1, typename T2>
bool compare(T1 a, T2 b) {
return a == b;
}
// 2. 类模板 Pair
template<typename T1, typename T2>
class Pair {
T1 firstVal;
T2 secondVal;
public:
Pair(T1 first, T2 second) : firstVal(first), secondVal(second) {}
T1 first() const { return firstVal; }
T2 second() const { return secondVal; }
};
// 3. 变参模板 printAll
template<typename... Args>
void printAll(Args... args) {
(std::cout << ... << (args << " ")) << std::endl;
}
int main() {
// 测试 compare 函数模板
std::cout << "Compare 10 and 10.0: " << compare(10, 10.0) << std::endl;
// 测试类模板 Pair
Pair<int, std::string> p(1, "Hello");
std::cout << "Pair: " << p.first() << ", " << p.second() << std::endl;
// 测试变参模板 printAll
printAll(1, 2.5, "Hello", 'A');
return 0;
}
9.6 小结
本节课学生学习了C++模板的基本概念以及如何通过泛型编程实现代码的复用和灵活性。模板使得编写通用的函数和类成为可能,而变参模板则进一步增强了模板的灵活性。在现代C++开发中,模板广泛应用于标准库和自定义容器、算法的设计。本节为后续的高级编程奠定了重要基础。
标签:std,函数,示例,int,cout,C++,入门 From: https://www.cnblogs.com/math/p/18441607/cpp