程序设计范式-Week2
程序设计范式-第2周小结
C++ 的面向对象 I/O
C 语言使用 printf 函数进行输出,它通过格式字符串指定输出格式.
问题与风险
1. 类型不安全
2. 编译器难以检查:
- 格式字符串是普通字符串,编译器很难验证参数类型是否匹配
- 错误通常在运行时才暴露,难以调试
3. 参数数量不匹配
C++ 的 IO 流:类型安全且可扩展
核心机制
1. 通过引用传递 (By References)
C++ I/O 使用引用而不是指针或值传递,这使得语法更简洁且更安全。
2. 函数重载 (Function Overloading)
C++ 为不同的数据类型重载了 << 和 >> 运算符,使它们能够处理各种内置类型。
3. 运算符重载 (Operator Overloading)
C++ 允许重载运算符,使自定义类型能够像内置类型一样使用 I/O 操作。
类型安全 (Type Safe)
C++ I/O 系统是类型安全的,这意味着编译器会在编译时检查类型匹配,防止运行时错误。
I/O 对数据类型的敏感性
C++ I/O 能够识别和处理不同的数据类型,为每种类型提供适当的格式化。
类型不匹配时的错误处理
当输入与期望类型不匹配时,C++ I/O 流会设置错误标志,程序可以检测并处理这些错误。
C++ 的可扩展性
C++ I/O 系统的最强大之处在于它的可扩展性。您可以为自己创建的任何类型重载 I/O 运算符。
综合示例:完整的自定义类型 I/O
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
#include <iostream>
#include <string>
using namespace std;
class Student {
private:
string name;
int id;
double gpa;
public:
Student(string n = "", int i = 0, double g = 0.0)
: name(n), id(i), gpa(g) {}
// 重载输出运算符
friend ostream& operator<<(ostream& os, const Student& s) {
os << "Student: " << s.name << " (ID: " << s.id
<< ", GPA: " << s.gpa << ")";
return os;
}
// 重载输入运算符
friend istream& operator>>(istream& is, Student& s) {
cout << "Enter student name: ";
getline(is, s.name);
cout << "Enter student ID: ";
is >> s.id;
cout << "Enter student GPA: ";
is >> s.gpa;
is.ignore(); // 忽略换行符
return is;
}
};
int main() {
Student s1("Alice", 12345, 3.8);
cout << s1 << endl;
Student s2;
cin >> s2;
cout << s2 << endl;
return 0;
}
数据类型
基本数据类型
C++ 提供了丰富的基本数据类型,它们可以分为几大类:
1. 字符类型 (char)
1
2
3
4
5
6
char letter = 'A'; // 单个字符
char number_char = '7'; // 字符 '7',不是数字 7
char newline = '\n'; // 转义字符表示换行符
// char 也可以表示小整数(通常 -128 到 127)
char small_number = 65; // 与 'A' 相同(ASCII 值)
2. 整数类型
1
2
3
4
5
6
7
8
9
short small_number = 100; // 通常 2 字节,-32,768 到 32,767
int normal_number = 100000; // 通常 4 字节,-2^31 到 2^31-1
long large_number = 1000000000L; // 通常 4 或 8 字节
long long very_large = 1000000000000LL; // 通常 8 字节
// 无符号版本
unsigned short us = 50000;
unsigned int ui = 4000000000U;
unsigned long ul = 4000000000UL;
3. 浮点类型
1
2
3
float f = 3.14159F; // 单精度,通常 4 字节
double d = 3.14159265358979; // 双精度,通常 8 字节
long double ld = 3.141592653589793238L; // 扩展精度,通常 10 或 16 字节
4. 布尔类型 (bool)
1
2
3
4
5
bool is_cpp_great = true;
bool is_c_better = false;
// 布尔值可以转换为整数(true=1, false=0)
int truth_value = is_cpp_great; // 值为 1
字面常量
字面常量是程序中直接书写的固定值:
1. 整数字面量
1
2
3
4
5
6
7
8
9
int decimal = 42; // 十进制
int octal = 052; // 八进制(前缀0),等于十进制的42
int hexadecimal = 0x2A; // 十六进制(前缀0x),等于十进制的42
// 带后缀的字面量
unsigned int ui = 42U; // 无符号整型
long l = 42L; // 长整型
unsigned long ul = 42UL; // 无符号长整型
long long ll = 42LL; // 长长整型
2. 浮点字面量
1
2
3
4
5
6
float f = 3.14F; // 单精度浮点数
double d = 3.14; // 双精度浮点数
long double ld = 3.14L; // 扩展精度浮点数
// 科学计数法
double sci = 6.022e23; // 6.022 × 10^23
3. 字符和字符串字面量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char c = 'A'; // 字符字面量
const char* str = "Hello, World!"; // C风格字符串字面量
// 转义序列
char newline = '\n'; // 换行符
char tab = '\t'; // 制表符
char quote = '\''; // 单引号
char backslash = '\\'; // 反斜杠
char null_char = '\0'; // 空字符
// 原始字符串字面量 (C++11)
const char* path = R"(C:\Program Files\MyApp)"; // 不需要转义反斜杠
const char* multi_line = R"(
Line 1
Line 2
Line 3
)"; // 多行字符串
C++ 字符串类型
C++ 提供了 std::string 类来处理字符串,比 C 风格的字符数组更安全、更方便:
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
#include <iostream>
#include <string>
using namespace std;
int main() {
// 初始化字符串的不同方式
string s1("Hello, World!"); // 从C风格字符串初始化
string s2(s1); // 从另一个string初始化
string s3(10, 'x'); // 包含10个'x'字符的字符串
string s4; // 空字符串
// 获取字符串长度
cout << "Length of s1: " << s1.size() << endl; // 13
cout << "Length of s4: " << s4.size() << endl; // 0
// 检查字符串是否为空
if (s4.empty()) {
cout << "s4 is empty" << endl;
}
// 字符串比较
string a = "apple";
string b = "banana";
if (a == b) {
cout << "Strings are equal" << endl;
} else {
cout << "Strings are different" << endl;
}
// 字符串赋值
s4 = s1; // 将s1的内容复制给s4
// 字符串连接
string greeting = "Hello";
string name = "Alice";
string message = greeting + ", " + name + "!"; // "Hello, Alice!"
cout << message << endl;
return 0;
}
结构化数据类型
1. 枚举类型 (enum)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 定义枚举类型
enum HealthType {Poor, Fair, Good, Excellent};
// 使用枚举
HealthType patient_health = Good;
// 枚举值可以转换为整数
int health_value = patient_health; // 值为2(从0开始)
// 有作用域的枚举 (C++11)
enum class Color {Red, Green, Blue};
Color favorite = Color::Blue;
// 需要显式转换才能获取整数值
// int color_value = static_cast<int>(favorite); // 值为2
2. 结构体类型 (struct)
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
// 定义结构体
struct AnimalType {
long id;
string name;
string genus;
string species;
string country;
int age;
float weight;
HealthType health;
};
// 使用结构体
AnimalType thisAnimal;
thisAnimal.id = 12345;
thisAnimal.name = "Leo";
thisAnimal.genus = "Panthera";
thisAnimal.species = "leo";
thisAnimal.country = "South Africa";
thisAnimal.age = 5;
thisAnimal.weight = 190.5f;
thisAnimal.health = Good;
// 初始化结构体 (C++11)
AnimalType anotherAnimal = {
67890, // id
"Tiger", // name
"Panthera", // genus
"tigris", // species
"India", // country
7, // age
220.0f, // weight
Excellent // health
};
// 访问结构体成员
cout << thisAnimal.name << " is a " << thisAnimal.species
<< " from " << thisAnimal.country << endl;
抽象的概念
抽象是计算机科学中的核心概念,指的是隐藏实现细节,只暴露必要的接口。在C++中,抽象通过多种机制实现:
1. 函数抽象
1
2
3
4
5
6
7
8
// 计算圆面积的函数 - 用户不需要知道计算细节
double calculateCircleArea(double radius) {
return 3.14159 * radius * radius;
}
// 使用函数
double area = calculateCircleArea(5.0);
// 用户不需要知道面积是如何计算的,只需要提供半径
2. 类抽象
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include <iostream>
#include <string>
using namespace std;
// 银行账户类 - 隐藏内部实现细节
class BankAccount {
private:
string accountNumber;
double balance;
// 私有方法 - 内部实现细节
bool isValidAmount(double amount) const {
return amount > 0;
}
public:
// 构造函数
BankAccount(const string& accNum, double initialBalance = 0.0)
: accountNumber(accNum), balance(initialBalance) {}
// 公共接口 - 用户只能通过这些方法访问账户
void deposit(double amount) {
if (isValidAmount(amount)) {
balance += amount;
cout << "Deposited: $" << amount << endl;
} else {
cout << "Invalid deposit amount" << endl;
}
}
bool withdraw(double amount) {
if (isValidAmount(amount) && amount <= balance) {
balance -= amount;
cout << "Withdrew: $" << amount << endl;
return true;
} else {
cout << "Invalid withdrawal amount or insufficient funds" << endl;
return false;
}
}
double getBalance() const {
return balance;
}
string getAccountNumber() const {
return accountNumber;
}
};
int main() {
// 使用银行账户类
BankAccount myAccount("123456789", 1000.0);
myAccount.deposit(500.0);
myAccount.withdraw(200.0);
cout << "Account " << myAccount.getAccountNumber()
<< " has balance: $" << myAccount.getBalance() << endl;
// 无法直接访问私有成员
// myAccount.balance = 1000000; // 编译错误
return 0;
}
类型转换
C++ 提供了多种类型转换机制:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 隐式转换
int i = 3.14; // 3 - 小数部分被截断
double d = i; // 3.0 - 整数转换为浮点数
// 显式转换(C风格)
double pi = 3.14159;
int approx_pi = (int)pi; // 3
// C++风格的类型转换(更安全)
int another_approx = static_cast<int>(pi); // 3
// 常量转换
const int ci = 10;
int* modifiable = const_cast<int*>(&ci); // 移除const限定符
// 重新解释转换(危险,但有时必要)
int number = 0x12345678;
char* bytes = reinterpret_cast<char*>(&number); // 将int解释为字节数组
抽象数据类型 (ADT) 与 C++ 类型定义
抽象数据类型 (ADT) 是计算机科学中的一个核心概念,它允许程序员创建具有特定行为和属性的自定义类型。下面是关于 ADT 和 C++ 中定义新类型的详细解释。
抽象数据类型 (ADT) 的概念
抽象数据类型 (ADT) 是一种程序员定义的类型,它具有:
-
一组特定的值(定义域)
-
一组允许在这些值上执行的操作
ADT 的关键特点是它将数据表示与操作实现分离,只向用户暴露接口,隐藏内部实现细节。
在 C++ 中定义新类型的方法
1. 使用 typedef
typedef 关键字为现有类型创建别名:
1
2
3
4
5
6
7
8
9
typedef char String20[21]; // 创建名为 String20 的类型,它是 21 个字符的数组
String20 message; // 声明一个 String20 类型的变量
// 使用示例
#include <cstring>
int main() {
strcpy(message, "Hello, World!"); // 复制字符串到 message
return 0;
}
2. 使用 struct
struct 允许将多个相关数据项组合成一个单一类型:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
struct EmployeeType {
long idNumber;
String20 name; // 使用之前定义的 String20 类型
};
EmployeeType myself; // 声明 EmployeeType 变量
// 使用示例
int main() {
myself.idNumber = 12345;
strcpy(myself.name, "John Doe");
std::cout << "Employee ID: " << myself.idNumber << std::endl;
std::cout << "Name: " << myself.name << std::endl;
return 0;
}
3. 使用 class(实现 ADT)
class 是 C++ 中实现 ADT 的主要方式,它允许将数据和操作封装在一起:
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <iostream>
#include <iomanip>
class TimeType {
private:
int hours;
int minutes;
int seconds;
public:
// 构造函数
TimeType() : hours(0), minutes(0), seconds(0) {}
TimeType(int h, int m, int s) : hours(h), minutes(m), seconds(s) {
normalize(); // 确保时间是有效的
}
// 设置时间
void setTime(int h, int m, int s) {
hours = h;
minutes = m;
seconds = s;
normalize();
}
// 打印时间
void printTime() const {
std::cout << std::setfill('0') << std::setw(2) << hours << ":"
<< std::setfill('0') << std::setw(2) << minutes << ":"
<< std::setfill('0') << std::setw(2) << seconds << std::endl;
}
// 增加一秒
void incrementByOneSecond() {
seconds++;
normalize();
}
// 比较两个时间是否相等
bool equals(const TimeType& other) const {
return (hours == other.hours) &&
(minutes == other.minutes) &&
(seconds == other.seconds);
}
private:
// 规范化时间(确保秒和分钟在0-59之间,小时在0-23之间)
void normalize() {
minutes += seconds / 60;
seconds %= 60;
if (seconds < 0) {
seconds += 60;
minutes--;
}
hours += minutes / 60;
minutes %= 60;
if (minutes < 0) {
minutes += 60;
hours--;
}
hours %= 24;
if (hours < 0) {
hours += 24;
}
}
};
// 使用示例
int main() {
TimeType t1(23, 59, 59);
TimeType t2;
std::cout << "Initial time: ";
t1.printTime();
t1.incrementByOneSecond();
std::cout << "After increment: ";
t1.printTime(); // 应该显示 00:00:00
t2.setTime(0, 0, 0);
std::cout << "t2: ";
t2.printTime();
if (t1.equals(t2)) {
std::cout << "t1 and t2 are equal" << std::endl;
} else {
std::cout << "t1 and t2 are not equal" << std::endl;
}
return 0;
}
TimeType ADT 的完整规范
更详细地定义 TimeType ADT:
TYPE
TimeType - 表示时间的数据类型,包含小时、分钟和秒。
DOMAIN
每个 TimeType 值是一个时间,由以下部分组成:
-
小时 (hours): 0 到 23 之间的整数
-
分钟 (minutes): 0 到 59 之间的整数
-
秒 (seconds): 0 到 59 之间的整数
OPERATIONS
-
设置时间 (setTime)
-
输入: 小时、分钟、秒
-
前置条件: 输入值在有效范围内
-
后置条件: 时间被设置为指定值
-
异常: 如果输入值无效,抛出异常或进行调整
-
-
打印时间 (printTime)
-
输入: 无
-
输出: 以 HH:MM:SS 格式打印时间
-
前置条件: 时间已正确初始化
-
后置条件: 无
-
-
增加一秒 (incrementByOneSecond)
-
输入: 无
-
前置条件: 时间已正确初始化
-
后置条件: 时间增加一秒,必要时处理进位(59秒→0秒,分钟+1)
-
-
比较相等 (equals)
-
输入: 另一个 TimeType 对象
-
输出: 布尔值,表示两个时间是否相等
-
前置条件: 两个时间都已正确初始化
-
后置条件: 无
-
C++ 引用传递
使用引用传递的三个主要原因
1. 修改传入函数的对象
引用允许函数直接修改调用者传递的变量,而不是操作副本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
// 使用引用修改传入的值
void increment(int &num) {
num++; // 直接修改原变量
}
int main() {
int x = 5;
cout << "Before increment: " << x << endl; // 输出 5
increment(x);
cout << "After increment: " << x << endl; // 输出 6
return 0;
}
2. 避免复制大对象的开销
对于大型对象(如结构体、类实例),使用引用可以避免复制整个对象的开销。
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
#include <iostream>
#include <string>
using namespace std;
struct LargeData {
int array[1000];
string text[100];
};
// 使用值传递 - 会产生复制开销
void processByValue(LargeData data) {
// 处理数据
}
// 使用引用传递 - 避免复制开销
void processByReference(const LargeData &data) {
// 处理数据,const确保不会意外修改
}
int main() {
LargeData bigData;
// 值传递 - 复制整个LargeData对象
processByValue(bigData);
// 引用传递 - 只传递引用,没有复制开销
processByReference(bigData);
return 0;
}
3. 实现多个返回值
通过引用参数,函数可以返回多个值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <cmath>
using namespace std;
// 通过引用参数返回多个值
void calculateCircle(double radius, double &area, double &circumference) {
area = M_PI * radius * radius;
circumference = 2 * M_PI * radius;
}
int main() {
double r = 5.0;
double a, c;
calculateCircle(r, a, c);
cout << "Radius: " << r << endl;
cout << "Area: " << a << endl;
cout << "Circumference: " << c << endl;
return 0;
}
引用与指针的区别
虽然引用和指针在底层机制上可能相似,但它们在用法上有重要区别:
引用特性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
int x = 10;
// 引用必须在声明时初始化
int &ref = x; // 正确:引用已初始化
// int &ref2; // 错误:引用必须初始化
// 引用不能重新绑定到其他变量
int y = 20;
// &ref = y; // 错误:不能重新绑定引用
// 引用是原变量的别名
ref = 15; // 等价于 x = 15
cout << "x = " << x << endl; // 输出 15
return 0;
}
指针特性
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main() {
int x = 10;
int y = 20;
// 指针可以不初始化(但不推荐)
int *ptr;
// 指针可以重新指向其他变量
ptr = &x; // 指向x
*ptr = 15; // x = 15
ptr = &y; // 现在指向y
*ptr = 25; // y = 25
// 指针可以为nullptr
ptr = nullptr;
return 0;
}
选择使用引用还是指针
-
使用引用:
-
你确定它总是代表一个非空对象
-
它不会改变代表其他对象
-
-
使用指针:
-
可能需要指向空值(nullptr)
-
需要能够重新指向不同的对象
-
引用参数在函数中的使用
基本语法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;
// 函数原型:data是一个引用到int
void modifyValue(int &data);
int main() {
int value = 10;
cout << "Before: " << value << endl;
modifyValue(value); // 调用格式与值传递相同
cout << "After: " << value << endl;
return 0;
}
// 函数定义
void modifyValue(int &data) {
data *= 2; // 修改原变量的值
}
常量引用参数
当不需要修改参数但想避免复制开销时,使用常量引用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <string>
using namespace std;
// 使用常量引用避免复制,同时防止修改
void printString(const string &str) {
cout << str << endl;
// str[0] = 'X'; // 错误:不能修改常量引用
}
int main() {
string longText = "This is a very long string that we want to avoid copying.";
printString(longText);
return 0;
}
返回引用
函数可以返回引用,但必须确保引用的对象在函数返回后仍然存在。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;
// 返回数组元素的引用
int &getElement(int arr[], int index) {
return arr[index]; // 返回引用,可以修改原数组
}
int main() {
int numbers[] = {1, 2, 3, 4, 5};
// 通过返回的引用修改数组
getElement(numbers, 2) = 10;
cout << "Modified array: ";
for (int i = 0; i < 5; i++) {
cout << numbers[i] << " ";
}
cout << endl; // 输出: 1 2 10 4 5
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
#include <iostream>
using namespace std;
// 使用引用参数
void swapByReference(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
// 使用指针参数
void swapByPointer(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int x = 5, y = 10;
cout << "Before swap: x = " << x << ", y = " << y << endl;
// 使用引用
swapByReference(x, y);
cout << "After reference swap: x = " << x << ", y = " << y << endl;
// 使用指针
swapByPointer(&x, &y);
cout << "After pointer swap: x = " << x << ", y = " << y << endl;
return 0;
}
C++ Vector(向量):数组的现代替代方案
传统array(数组)缺点
C++ 中的传统数组确实有很多缺点:
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
// 数组的问题示例
void demonstrateArrayProblems() {
// 1. 固定大小
const int SIZE = 5;
int arr[SIZE] = {1, 2, 3, 4, 5};
// 无法动态调整大小
// arr[5] = 6; // 错误:数组越界
// 2. 传递数组给函数很麻烦
printArray(arr, SIZE); // 必须传递大小参数
// 3. 插入和删除元素困难
// 要在中间插入元素,需要手动移动后面的所有元素
// 要删除元素,同样需要移动大量元素
// 4. 不能从函数返回数组
// int[] createArray() { return {1,2,3}; } // 错误
}
// 必须传递大小参数
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
cout << arr[i] << " ";
}
cout << endl;
}
Vector 的基本用法
Vector 是 C++ 标准模板库(STL)中的动态数组,解决了传统数组的所有问题。
- 包含头文件和声明
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
#include <iostream>
#include <vector> // 必须包含这个头文件
using namespace std;
int main() {
// 声明向量的不同方式
// 1. 声明包含 20 个 int 的向量
const int SIZE = 20;
vector<int> numbers1(SIZE);
// 2. 声明包含 20 个 int 的向量,所有元素初始化为 18
vector<int> numbers2(SIZE, 18);
// 3. 声明空向量
vector<int> numbers3;
// 4. 使用初始化列表(C++11)
vector<int> numbers4 = {1, 2, 3, 4, 5};
// 5. 从另一个向量复制
vector<int> numbers5(numbers4);
return 0;
}
Vector 的初始化细节
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 默认初始化:元素为默认值(int 为 0)
vector<int> v1(5); // 5 个 0: {0, 0, 0, 0, 0}
// 指定初始值:所有元素初始化为指定值
vector<int> v2(5, 18); // 5 个 18: {18, 18, 18, 18, 18}
// 注意:如果类型不匹配会发生隐式转换
vector<int> v3(5, 3.5); // 5 个 3: {3, 3, 3, 3, 3}(小数部分被截断)
// 初始化列表(C++11)
vector<int> v4 = {1, 2, 3, 4, 5}; // {1, 2, 3, 4, 5}
// 空向量
vector<int> v5; // 空向量
return 0;
}
Vector 的常用方法
- 基本操作方法
```cpp
#include
#include using namespace std;
int main() {
vector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 1. size() - 获取元素数量
cout << "Size: " << numbers.size() << endl; // 输出 3
// 2. empty() - 检查是否为空
cout << "Is empty: " << (numbers.empty() ? "Yes" : "No") << endl; // 输出 No
// 3. resize() - 调整大小
numbers.resize(5); // 调整为 5 个元素,新元素初始化为 0
cout << "After resize to 5: ";
for (int num : numbers) cout << num << " "; // 输出 1 2 3 0 0
cout << endl;
// 可以指定新元素的默认值
numbers.resize(7, 99); // 调整为 7 个元素,新元素初始化为 99
cout << "After resize to 7 with default 99: ";
for (int num : numbers) cout << num << " "; // 输出 1 2 3 0 0 99 99
cout << endl;
// 4. clear() - 清空向量
numbers.clear();
cout << "After clear - Size: " << numbers.size() << endl; // 输出 0
cout << "Is empty: " << (numbers.empty() ? "Yes" : "No") << endl; // 输出 Yes
return 0; } ```
- 添加和删除元素
```cpp
#include
#include using namespace std;
int main() {
vector
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
// push_back() - 在末尾添加元素
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);
cout << "After push_back: ";
for (int num : numbers) cout << num << " "; // 输出 10 20 30
cout << endl;
// pop_back() - 删除末尾元素
numbers.pop_back();
cout << "After pop_back: ";
for (int num : numbers) cout << num << " "; // 输出 10 20
cout << endl;
// insert() - 在指定位置插入元素
numbers.insert(numbers.begin() + 1, 15); // 在索引 1 的位置插入 15
cout << "After insert at index 1: ";
for (int num : numbers) cout << num << " "; // 输出 10 15 20
cout << endl;
// erase() - 删除指定位置的元素
numbers.erase(numbers.begin()); // 删除第一个元素
cout << "After erase first element: ";
for (int num : numbers) cout << num << " "; // 输出 15 20
cout << endl;
return 0; } ``` ### Vector 的高级特性 - 访问元素的不同方式 ```cpp #include <iostream> #include <vector> using namespace std;
int main() {
vector
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 1. 使用下标运算符 [](不检查边界)
cout << "Element at index 2: " << numbers[2] << endl; // 30
// 2. 使用 at() 方法(检查边界,更安全)
cout << "Element at index 2: " << numbers.at(2) << endl; // 30
// at() 会在越界时抛出异常
try {
cout << "Element at index 10: " << numbers.at(10) << endl;
} catch (const out_of_range& e) {
cout << "Error: " << e.what() << endl;
}
// 3. 访问第一个和最后一个元素
cout << "First element: " << numbers.front() << endl; // 10
cout << "Last element: " << numbers.back() << endl; // 50
return 0; } ```
- Vector 作为函数参数和返回值
```cpp
#include
#include using namespace std;
// Vector 可以作为函数参数(推荐使用常量引用避免复制)
void printVector(const vector
// Vector 可以作为函数返回值
vector
// 修改 vector 内容的函数
void doubleValues(vector
int main() {
// 测试作为返回值的函数
vector
1
2
3
4
5
// 测试修改 vector 的函数
doubleValues(sequence);
printVector(sequence); // 输出 2 4 6 8 10
return 0; } ```
函数的基本结构
- 函数声明、定义和调用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 1. 函数声明(可以出现多次)
int functionA(int iX, int iY);
// 2. 函数定义(只能出现一次)
int functionA(int iX, int iY) {
return iX + iY;
}
int main() {
// 3. 函数调用
int result = functionA(3, 5); // 3和5是实参(arguments)
cout << "Result: " << result << endl; // 输出 8
return 0;
}
例:斐波那契数列函数
- 基础版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
int fibon_elem(int iPos) {
if (iPos <= 0) return 0;
if (iPos == 1 || iPos == 2) return 1;
int n2 = 1, n1 = 1;
int iElem = 1;
for (int iX = 3; iX <= iPos; iX++) {
iElem = n2 + n1;
n2 = n1;
n1 = iElem;
}
return iElem;
}
错误处理与输入验证
- 改进的斐波那契函数(带错误检查)
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
45
46
47
48
49
50
51
52
53
54
55
56
57
// 改进版本:返回布尔值表示成功/失败,通过引用参数返回结果
bool fibon_elem(int iPos, int &iElem) {
// 输入验证:防止无效位置
if (iPos <= 0 || iPos >= 1024) {
iElem = 0;
return false; // 表示计算失败
}
if (iPos == 1 || iPos == 2) {
iElem = 1;
return true;
}
int n2 = 1, n1 = 1;
iElem = 1;
for (int iX = 3; iX <= iPos; iX++) {
iElem = n2 + n1;
// 检查整数溢出
if (iElem < 0) { // 如果出现负数,说明溢出
iElem = 0;
return false;
}
n2 = n1;
n1 = iElem;
}
return true;
}
int main() {
int result;
// 有效输入
if (fibon_elem(10, result)) {
cout << "Fibonacci(10) = " << result << endl;
} else {
cout << "Invalid position or overflow!" << endl;
}
// 无效输入
if (fibon_elem(2000, result)) {
cout << "Fibonacci(2000) = " << result << endl;
} else {
cout << "Invalid position or overflow!" << endl;
}
// 边界情况
if (fibon_elem(0, result)) {
cout << "Fibonacci(0) = " << result << endl;
} else {
cout << "Invalid position!" << 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
#include <iostream>
#include <vector>
using namespace std;
// 错误版本:返回局部变量的指针
vector<int>* fibon_seq_bad(int iLength) {
if (iLength <= 0 || iLength >= 1024) {
cerr << "Length " << iLength
<< " not supported, reset to 8" << endl;
iLength = 8;
}
vector<int> Elems(iLength); // 局部变量,函数结束后会被销毁
for (int iX = 0; iX < iLength; iX++) {
if (iX == 0 || iX == 1)
Elems[iX] = 1;
else
Elems[iX] = Elems[iX-1] + Elems[iX-2];
}
return &Elems; // 危险!返回指向即将被销毁对象的指针
}
// 使用这个函数会导致未定义行为
void demonstrate_bad_example() {
vector<int>* bad_ptr = fibon_seq_bad(10);
// 此时 bad_ptr 指向的内存已经无效
// 访问它是未定义行为
// cout << (*bad_ptr)[0] << endl; // 可能崩溃或输出错误数据
}
作用域(Extent)的概念
- C++ 中有几种不同的作用域:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <vector>
using namespace std;
// 文件作用域(全局变量)- 不推荐大量使用
vector<int> globalElems; // 在程序启动时分配,结束时销毁
void demonstrate_extents() {
// 局部作用域(栈上分配)
int localVar = 42; // 函数结束时自动销毁
// 动态作用域(堆上分配)
int* dynamicVar = new int(100); // 需要手动管理
delete dynamicVar; // 必须手动释放
}
正确的内存管理:使用 new 和 delete
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void demonstrate_new_delete() {
// 单个对象
string* strPtr = new string("Hello");
cout << *strPtr << endl;
delete strPtr; // 删除单个对象
// 对象数组
string* strArray = new string[5];
for (int i = 0; i < 5; i++) {
strArray[i] = "String " + to_string(i);
}
delete[] strArray; // 删除数组(必须使用 delete[])
// 错误的混合使用示例
// int* badArray = new int[10];
// delete badArray; // 错误!应该使用 delete[]
// 这会导致未定义行为,可能内存泄漏
}
- Fibonacci例子
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
#include <iostream>
#include <vector>
#include <string>
using namespace std;
// 正确的动态内存分配
vector<int>* create_fibon_seq(int iLength) {
if (iLength <= 0 || iLength >= 1024) {
return nullptr; // 返回空指针表示失败
}
// 使用 new 在堆上分配内存
vector<int>* Elems = new vector<int>(iLength);
for (int iX = 0; iX < iLength; iX++) {
if (iX == 0 || iX == 1)
(*Elems)[iX] = 1;
else
(*Elems)[iX] = (*Elems)[iX-1] + (*Elems)[iX-2];
}
return Elems; // 返回堆上对象的指针
}
// 使用函数
void use_dynamic_memory() {
vector<int>* seq = create_fibon_seq(10);
if (seq != nullptr) {
cout << "Fibonacci sequence: ";
for (int num : *seq) {
cout << num << " ";
}
cout << endl;
// 必须手动释放内存
delete seq;
}
}
优化方案:使用静态局部变量
- 使用静态局部变量缓存结果
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
45
46
47
48
#include <iostream>
#include <vector>
using namespace std;
// 优化版本:使用静态局部变量缓存计算结果
const vector<int>* fibon_seq(int iLength) {
// 静态局部变量:在第一次调用时初始化,之后保持状态
static vector<int> Elems;
if (iLength <= 0 || iLength > 1024) {
cerr << "Length " << iLength << " not supported" << endl;
return nullptr;
}
// 如果请求的长度大于当前缓存的大小,扩展缓存
if (iLength > Elems.size()) {
for (int iX = Elems.size(); iX < iLength; iX++) {
if (iX == 0 || iX == 1)
Elems.push_back(1);
else
Elems.push_back(Elems[iX-1] + Elems[iX-2]);
}
}
return &Elems; // 返回指向静态变量的指针(安全)
}
void use_static_version() {
// 第一次调用会计算所有元素
const vector<int>* seq1 = fibon_seq(10);
if (seq1) {
cout << "First 10: ";
for (int i = 0; i < 10; i++) {
cout << (*seq1)[i] << " ";
}
cout << endl;
}
// 第二次调用会复用之前计算的结果
const vector<int>* seq2 = fibon_seq(15);
if (seq2) {
cout << "First 15: ";
for (int i = 0; i < 15; i++) {
cout << (*seq2)[i] << " ";
}
cout << endl;
}
}
代码重构与内联函数
- 将功能拆分为小函数
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
45
46
#include <iostream>
#include <vector>
using namespace std;
// 内联函数:长度验证(建议编译器内联展开)
inline bool is_size_ok(int iLength) {
if (iLength <= 0 || iLength > 1024) {
cerr << "Length " << iLength << " not supported" << endl;
return false;
}
return true;
}
// 计算并扩展斐波那契序列
void fibon_expand(vector<int>& Elems, int iLength) {
for (int iX = Elems.size(); iX < iLength; iX++) {
if (iX == 0 || iX == 1)
Elems.push_back(1);
else
Elems.push_back(Elems[iX-1] + Elems[iX-2]);
}
}
// 重构后的主函数
const vector<int>* fibon_seq_refactored(int iLength) {
static vector<int> Elems;
if (!is_size_ok(iLength)) {
return nullptr;
}
fibon_expand(Elems, iLength);
return &Elems;
}
// 值传递版本(简单但可能效率较低)
vector<int> fibon_seq_by_value(int iLength) {
vector<int> Elems;
if (!is_size_ok(iLength)) {
return Elems; // 返回空向量
}
fibon_expand(Elems, iLength);
return Elems; // 返回副本
}