

数组是相同类型数据元素的有序集合,在内存中连续存储,通过下标快速访问。数组是 C++ 中批量处理数据的基础工具。
数组的声明需要指定数据类型、数组名和元素个数(数组大小)。
// 一维数组声明
数据类型 数组名[数组大小];
// 二维数组声明(可理解为"数组的数组")
数据类型 数组名[行数][列数];#include <iostream>
using namespace std;
int main() {
// 声明一维数组(存储5个int类型元素)
int scores[5];
// 赋值(下标从0开始,范围0~4)
scores[0] = 90;
scores[1] = 85;
scores[2] = 92;
scores[3] = 88;
scores[4] = 95;
// 访问元素并输出
cout << "第3个成绩:" << scores[2] << endl; // 输出92
// 二维数组(3行2列)
int matrix[3][2] = {{1,2}, {3,4}, {5,6}};
cout << "第二行第一列元素:" << matrix[1][0] << endl; // 输出3
return 0;
}
⚠️ 注意:数组下标从 0 开始,最大下标为 “数组大小 - 1”。访问
scores[5]会导致下标越界,可能破坏内存数据或程序崩溃。
数组在内存中是连续存储的,每个元素占用相同大小的内存空间(由数据类型决定)。
内存地址:0x1000 0x1004 0x1008 0x100C 0x1010
元素值: 90 85 92 88 95
下标: 0 1 2 3 4(int 类型占 4 字节,所以地址间隔 4)
#include <iostream>
using namespace std;
int main() {
// 1. 完全初始化:指定所有元素值
int arr1[3] = {10, 20, 30};
// 2. 部分初始化:未指定的元素自动为0(全局数组默认初始化为0,局部数组不初始化则值随机)
int arr2[5] = {1, 2}; // 元素为[1,2,0,0,0]
// 3. 省略大小:编译器根据初始化元素个数自动确定大小
int arr3[] = {5, 6, 7, 8}; // 大小为4
// 4. 二维数组初始化:按行初始化
int matrix[2][3] = {{1,2,3}, {4,5,6}}; // 2行3列
// 打印arr3验证
for(int i=0; i<4; i++){
cout << arr3[i] << " "; // 输出:5 6 7 8
}
return 0;
}
数组作为函数参数时,本质是传递数组首地址(不是整个数组拷贝),因此函数内部修改数组会影响原数组。由于数组传参不携带大小信息,通常需要额外传递数组长度。
// 形参写法1:数组形式([]内的大小可省略)
void func(int arr[], int len);
// 形参写法2:指针形式(与数组形式等价)
void func(int* arr, int len);#include <iostream>
using namespace std;
// 计算数组元素之和(数组传参+长度)
int sumArray(int arr[], int len) { // 等价于int* arr
int sum = 0;
for(int i=0; i<len; i++){
sum += arr[i];
}
return sum;
}
// 修改数组元素(演示传址修改)
void doubleArray(int* arr, int len) {
for(int i=0; i<len; i++){
arr[i] *= 2; // 等价于*(arr+i) *= 2
}
}
int main() {
int nums[] = {1,2,3,4,5};
int len = sizeof(nums)/sizeof(nums[0]); // 计算数组长度
cout << "原数组和:" << sumArray(nums, len) << endl; // 输出15
doubleArray(nums, len);
cout << "翻倍后数组和:" << sumArray(nums, len) << endl; // 输出30
return 0;
}
对象数组是元素为对象的数组,每个元素都是类的实例。声明时需确保类有合适的构造函数,初始化时会自动调用构造函数。
#include <iostream>
#include <string>
using namespace std;
// 学生类
class Student {
private:
string name;
int age;
public:
// 构造函数
Student() : name("未知"), age(0) {} // 默认构造
Student(string n, int a) : name(n), age(a) {} // 带参构造
// 打印信息
void showInfo() {
cout << "姓名:" << name << ",年龄:" << age << endl;
}
};
int main() {
// 声明对象数组(3个Student对象)
Student stuArray[3] = {
Student("张三", 18), // 调用带参构造
Student("李四", 19),
Student() // 调用默认构造
};
// 访问对象数组元素
for(int i=0; i<3; i++){
cout << "学生" << i+1 << ":";
stuArray[i].showInfo(); // 调用成员函数
}
return 0;
}输出:

需求:输入 5 名学生的成绩,统计平均分、最高分、最低分,并输出成绩等级(A:90+, B:80-89, C:70-79, D:60-69, E:60-)。
#include <iostream>
#include <iomanip> // 用于setprecision
using namespace std;
// 统计函数:返回平均分,通过指针输出最高分和最低分
double statScores(int scores[], int len, int* maxScore, int* minScore) {
int sum = 0;
*maxScore = scores[0];
*minScore = scores[0];
for(int i=0; i<len; i++){
sum += scores[i];
if(scores[i] > *maxScore) *maxScore = scores[i];
if(scores[i] < *minScore) *minScore = scores[i];
}
return (double)sum / len;
}
// 获取成绩等级
char getGrade(int score) {
if(score >= 90) return 'A';
else if(score >= 80) return 'B';
else if(score >= 70) return 'C';
else if(score >= 60) return 'D';
else return 'E';
}
int main() {
const int STUDENT_NUM = 5; // 学生数量
int scores[STUDENT_NUM];
int maxScore, minScore;
// 输入成绩
cout << "请输入" << STUDENT_NUM << "名学生的成绩:" << endl;
for(int i=0; i<STUDENT_NUM; i++){
cin >> scores[i];
}
// 统计
double avg = statScores(scores, STUDENT_NUM, &maxScore, &minScore);
// 输出结果
cout << "\n成绩统计结果:" << endl;
cout << "平均分:" << fixed << setprecision(1) << avg << endl;
cout << "最高分:" << maxScore << endl;
cout << "最低分:" << minScore << endl;
// 输出每个学生的等级
cout << "\n各学生成绩等级:" << endl;
for(int i=0; i<STUDENT_NUM; i++){
cout << "第" << i+1 << "名:" << scores[i] << "分 → " << getGrade(scores[i]) << endl;
}
return 0;
}运行示例:

指针是存储内存地址的变量,它让程序能间接访问内存中的数据,是 C++ 灵活性和高效性的核心特性之一。
计算机中每个内存单元都有唯一地址,数据存储在内存中。C++ 有两种访问方式:
int a=10; cout<<a;)int* p=&a; cout<<*p;)
1. 栈对象访问 (自动存储期)
void function() {
int x = 10; // 直接值访问
MyClass obj; // 自动构造
obj.method(); // 成员访问
x = obj.value; // 对象值复制
} // 自动析构obj并释放栈内存2. 堆对象访问 (动态存储期)
MyClass* create() {
MyClass* p = new MyClass(); // 堆分配
p->method(); // 指针成员访问
(*p).value = 20; // 解引用访问
return p;
}
void use() {
MyClass* ptr = create();
delete ptr; // 必须显式释放
}ptr->member
*ptr
unique_ptr<MyClass> up(new MyClass());
3. 全局/静态对象访问
int global = 5; // 全局变量
void func() {
static int count = 0; // 静态局部变量
count++;
global = count; // 直接访问
}
class Singleton {
static Singleton& get() {
static Singleton instance; // 静态成员
return instance;
}
};4. 类成员访问
class Container {
public:
int value; // 栈成员
int* data; // 堆成员指针
Container(int size)
: value(0), data(new int[size]) {} // 堆分配
~Container() { delete[] data; } // 必须释放
void process() {
value++; // 直接成员访问
data[0] = value;// 堆成员访问
}
};obj.member
obj.ptr->x
obj.ref = y
访问方式 | 安全实践 | 风险提示 |
|---|---|---|
栈访问 | 保持小对象(<1KB) | 栈溢出(stack overflow) |
堆访问 | 使用智能指针(unique_ptr/shared_ptr) | 内存泄漏/野指针 |
全局访问 | 避免跨编译单元依赖初始化顺序 | 未初始化访问 |
类成员访问 | 遵循RAII原则,在构造函数中初始化 | 空指针访问/浅拷贝问题 |
指针访问 | 访问前检查 nullptr | 段错误(segmentation fault) |
// 值访问(栈复制)
int a = obj.value;
// 指针访问(堆间接)
int* p = &obj.value;
*p = 100;
// 引用访问(别名)
int& ref = obj.value;
ref = 200;
// 数组成员访问
int arr[5];
arr[2] = 300;
// 智能指针访问
auto uptr = make_unique<MyClass>();
uptr->method(); 最佳实践:优先使用栈对象和智能指针,避免裸指针操作。使用Valgrind/AddressSanitizer检测内存错误,遵循RAII原则管理资源。
指针变量的声明需要指定基类型(指针指向的数据类型),语法:基类型* 指针名;
*表示该变量是指针,与变量名绑定(int* p1,p2;中 p1 是指针,p2 是 int 变量)#include <iostream>
using namespace std;
int main() {
int a = 100;
double b = 3.14;
char c = 'A';
// 声明指针并指向对应变量
int* pInt = &a; // int类型指针,指向a
double* pDouble = &b;// double类型指针,指向b
char* pChar = &c; // char类型指针,指向c
cout << "pInt指向的值:" << *pInt << endl; // 输出100
cout << "pDouble指向的值:" << *pDouble << endl;// 输出3.14
cout << "pChar指向的值:" << *pChar << endl; // 输出A
return 0;
}
&:取地址运算符,返回变量的内存地址(如&a返回 a 的地址)*:解引用运算符,返回指针指向地址中的值(如*p返回 p 指向的值)#include <iostream>
using namespace std;
int main() {
int x = 20;
int* p; // 声明指针p
p = &x; // p存储x的地址(&x)
cout << "x的值:" << x << endl; // 输出20
cout << "x的地址:" << &x << endl; // 输出x的内存地址(如0x7ffd9a8b8a4c)
cout << "p存储的地址:" << p << endl; // 输出与&x相同
cout << "p指向的值:" << *p << endl; // 输出20(解引用p)
*p = 30; // 通过指针修改x的值
cout << "修改后x的值:" << x << endl; // 输出30
return 0;
}
指针可以通过赋值指向不同变量,但需确保类型兼容(或强制转换)。常见赋值场景:
int* p=&a;int arr[5]; int* p=&arr[0];(等价于p=arr;,数组名是首地址)int* p=NULL; 或 int* p=nullptr;(C++11 推荐 nullptr,指向空地址)#include <iostream>
#include <cstdio> // 引入NULL的定义
using namespace std;
int main() {
int a = 10, b = 20;
int arr[] = {1,2,3};
int* p = &a; // 指向a
cout << "p指向a:" << *p << endl; // 10
p = &b; // 重新指向b
cout << "p指向b:" << *p << endl; // 20
p = arr; // 指向数组首元素(等价于&arr[0])
cout << "p指向arr[0]:" << *p << endl; // 1
p++; // 指针后移(指向arr[1])
cout << "p指向arr[1]:" << *p << endl; // 2
p = NULL; // 使用NULL代替nullptr,兼容C++98标准
// cout << *p; // 错误:解引用空指针会崩溃
return 0;
}
指针支持有限的运算:加减运算、比较运算,运算规则与基类型相关。
p1 < p2判断地址前后)#include <iostream>
using namespace std;
int main() {
int arr[] = {10,20,30,40,50};
int* p = arr; // 指向arr[0](地址假设为0x1000)
cout << "p指向的值:" << *p << endl; // 10(0x1000)
p += 2; // 移动2个int(8字节),指向arr[2](0x1008)
cout << "p+2指向的值:" << *p << endl; // 30
int* pEnd = &arr[4]; // 指向arr[4](0x1010)
cout << "pEnd - p = " << pEnd - p << endl; // 结果2(arr[4]-arr[2]有2个元素)
// 比较指针地址
if(p < pEnd) {
cout << "p在pEnd前面" << endl; // 输出此句
}
return 0;
}
数组名本质是首元素地址常量,指针与数组下标可互换使用:arr[i] 等价于 *(arr+i) 等价于 *(p+i)(p 是指向 arr 的指针)。
#include <iostream>
using namespace std;
int main() {
int nums[] = {1,2,3,4,5};
int len = sizeof(nums)/sizeof(nums[0]);
// 方法1:下标法(直观)
cout << "下标法遍历:";
for(int i=0; i<len; i++){
cout << nums[i] << " ";
}
cout << endl;
// 方法2:指针法(高效)
cout << "指针法遍历:";
int* p = nums; // p指向首元素
for(int i=0; i<len; i++){
cout << *(p+i) << " "; // 等价于p[i]
}
cout << endl;
// 方法3:指针自增遍历
cout << "指针自增遍历:";
for(p = nums; p < nums + len; p++){ // 指针范围:首地址 ~ 尾地址
cout << *p << " ";
}
cout << endl;
return 0;
}
指针数组是元素为指针的数组,语法:基类型* 数组名[大小];。常用于存储多个字符串(避免存储冗余)。
#include <iostream>
using namespace std;
int main() {
// 指针数组:每个元素是char*(字符串指针)
const char* languages[] = {
"C++",
"Python",
"Java",
"JavaScript"
};
int len = sizeof(languages)/sizeof(languages[0]);
// 遍历指针数组
cout << "编程语言列表:" << endl;
for(int i=0; i<len; i++){
cout << i+1 << ". " << languages[i] << endl; // 输出字符串
}
return 0;
}输出:

指针作为函数参数时,函数可通过指针间接修改实参的值,实现 “返回多个结果” 的效果。
#include <iostream>
using namespace std;
// 交换a和b的值(通过指针修改实参)
void swap(int* p1, int* p2) {
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
// 计算和,并通过指针返回差
int sumAndDiff(int a, int b, int* pDiff) {
*pDiff = a - b; // 差通过指针返回
return a + b; // 和通过返回值返回
}
int main() {
int x = 10, y = 20;
cout << "交换前:x=" << x << ", y=" << y << endl; // x=10, y=20
swap(&x, &y);
cout << "交换后:x=" << x << ", y=" << y << endl; // x=20, y=10
int diff;
int sum = sumAndDiff(x, y, &diff);
cout << "x+y=" << sum << ", x-y=" << diff << endl; // 30, 10
return 0;
}
指针型函数是返回值为指针的函数,语法:基类型* 函数名(参数列表);。常用于返回动态分配的内存或数组元素地址。
#include <iostream>
#include <cstdio> // 引入NULL的定义
using namespace std;
// 指针型函数:返回数组中最大值的地址
int* findMax(int arr[], int len) {
if(len <= 0) return NULL; // 使用NULL替代nullptr
int* pMax = &arr[0]; // 假设首元素最大
for(int i=1; i<len; i++){
if(arr[i] > *pMax){
pMax = &arr[i]; // 更新最大值地址
}
}
return pMax;
}
int main() {
int nums[] = {5, 2, 9, 1, 7};
int len = sizeof(nums)/sizeof(nums[0]);
int* pMax = findMax(nums, len);
if(pMax != NULL){ // 使用NULL替代nullptr
cout << "最大值:" << *pMax << endl; // 输出9
cout << "最大值位置(下标):" << pMax - nums << endl; // 输出2(第3个元素)
}
return 0;
}
指向函数的指针是存储函数地址的指针,可通过指针调用函数,常用于回调函数场景。
返回类型 (*指针名)(参数类型列表);#include <iostream>
#include <cstdio> // 引入NULL的定义
using namespace std;
// 加法
int add(int a, int b) {
return a + b;
}
// 减法
int sub(int a, int b) {
return a - b;
}
// 乘法
int mul(int a, int b) {
return a * b;
}
// 通过函数指针调用运算
void calculate(int a, int b, int (*op)(int, int)) {
if(op != NULL){ // 使用NULL替代nullptr
cout << "结果:" << op(a, b) << endl; // 调用指针指向的函数
}
}
int main() {
int x = 10, y = 5;
// 声明函数指针并指向不同函数
int (*pOp)(int, int);
pOp = add; // 指向加法
calculate(x, y, pOp); // 输出15
pOp = sub; // 指向减法
calculate(x, y, pOp); // 输出5
pOp = mul; // 指向乘法
calculate(x, y, pOp); // 输出50
return 0;
}
对象指针是指向类对象的指针,通过->运算符访问对象成员(等价于(*指针).成员)。
#include <iostream>
#include <string>
using namespace std;
class Person {
private:
string name;
public:
Person(string n) : name(n) {}
void setName(string n) { name = n; }
string getName() { return name; }
};
int main() {
Person p1("Alice"); // 创建对象
Person* pPtr = &p1; // 对象指针指向p1
// 访问成员:两种方式等价
cout << "姓名:" << pPtr->getName() << endl; // ->运算符
cout << "姓名:" << (*pPtr).getName() << endl; // 解引用+ .运算符
pPtr->setName("Bob"); // 修改成员
cout << "修改后姓名:" << p1.getName() << endl; // 输出Bob
// 动态创建对象(后续讲动态内存分配)
Person* pNew = new Person("Charlie");
cout << "动态对象姓名:" << pNew->getName() << endl; // 输出Charlie
delete pNew; // 释放内存
return 0;
}
静态内存(如局部变量、全局变量)由编译器自动分配和释放,动态内存由程序员通过new和delete手动管理,用于灵活控制内存大小(如运行时确定数组大小)。
new 类型:分配单个元素内存,返回指向该元素的指针new 类型[大小]:分配数组内存,返回首元素指针delete 指针:释放单个元素内存delete[] 指针:释放数组内存(必须与 new [] 配对)#include <iostream>
#include <cstdio> // 引入NULL的定义
using namespace std;
int main() {
// 动态分配单个int
int* pInt = new int; // 分配4字节内存
*pInt = 100;
cout << "动态int值:" << *pInt << endl;
delete pInt; // 释放内存,避免泄漏
pInt = NULL; // 用NULL替代nullptr,置空指针
// 动态分配数组(大小运行时确定)
int n;
cout << "请输入数组大小:";
cin >> n;
int* pArr = new int[n]; // 分配n个int的内存
if(pArr == NULL){ // 用NULL替代nullptr,检查内存分配失败
cout << "内存分配失败!" << endl;
return 1;
}
// 给动态数组赋值
for(int i=0; i<n; i++){
pArr[i] = i * 10;
}
// 打印数组
cout << "动态数组元素:";
for(int i=0; i<n; i++){
cout << pArr[i] << " ";
}
cout << endl;
delete[] pArr; // 释放数组内存(必须用delete[])
pArr = NULL; // 用NULL替代nullptr,置空指针
return 0;
}
⚠️ 注意:
vector是 C++ 标准库提供的动态数组容器,自动管理内存(无需手动 new/delete),支持动态扩容,比原生数组更安全、便捷。
操作 | 说明 |
|---|---|
vector<类型> v; | 声明空 vector |
vector<类型> v(n, val); | 声明 n 个元素,初始值为 val |
v.push_back(val); | 尾部添加元素 |
v.size(); | 获取元素个数 |
v[i]; | 访问第 i 个元素(下标越界会崩溃) |
v.at(i); | 访问第 i 个元素(越界抛异常) |
v.begin(); | 返回首元素迭代器(类似指针) |
v.end(); | 返回尾后迭代器 |
#include <iostream>
#include <vector> // 需包含头文件
using namespace std;
int main() {
// 创建vector并初始化
vector<int> nums; // 空vector
vector<string> fruits = {"苹果", "香蕉", "橙子"}; // C++11初始化列表
// 尾部添加元素
nums.push_back(10);
nums.push_back(20);
nums.push_back(30);
// 访问元素
cout << "fruits第2个元素:" << fruits[1] << endl; // 输出香蕉
cout << "nums元素个数:" << nums.size() << endl; // 输出3
// 遍历vector(三种方式)
cout << "nums元素(下标):";
for(int i=0; i<nums.size(); i++){
cout << nums[i] << " "; // 10 20 30
}
cout << endl;
cout << "nums元素(迭代器):";
for(auto it = nums.begin(); it != nums.end(); it++){ // it是迭代器(类似指针)
cout << *it << " "; // 10 20 30
}
cout << endl;
cout << "nums元素(范围for):";
for(int num : nums){ // C++11范围for
cout << num << " "; // 10 20 30
}
cout << endl;
// 动态数组优势:自动扩容
nums.push_back(40);
cout << "添加后size:" << nums.size() << endl; // 输出4
return 0; // vector自动释放内存,无需手动操作
}
当类中包含指针成员时,默认的拷贝操作(拷贝构造函数、赋值运算符)是浅层复制(仅复制指针地址,不复制指针指向的内容),可能导致 “同一块内存被释放两次” 的问题。深层复制会复制指针指向的内容,避免此问题。
#include <iostream>
#include <cstring> // 字符串函数
using namespace std;
class MyString {
private:
char* str; // 指针成员
public:
// 构造函数
MyString(const char* s = "") {
int len = strlen(s) + 1; // 包含'\0'
str = new char[len]; // 分配内存
strcpy(str, s); // 复制字符串
}
// 浅层复制:默认拷贝构造(编译器生成)会导致问题
// MyString(const MyString& other) : str(other.str) {} // 危险!
// 深层复制:自定义拷贝构造
MyString(const MyString& other) {
int len = strlen(other.str) + 1;
str = new char[len]; // 新分配内存
strcpy(str, other.str); // 复制内容
}
// 浅层复制:默认赋值运算符(编译器生成)
// MyString& operator=(const MyString& other) {
// if(this != &other) str = other.str; // 危险!
// return *this;
// }
// 深层复制:自定义赋值运算符
MyString& operator=(const MyString& other) {
if(this != &other) { // 避免自赋值
delete[] str; // 释放原有内存
int len = strlen(other.str) + 1;
str = new char[len]; // 新分配内存
strcpy(str, other.str); // 复制内容
}
return *this;
}
// 析构函数:释放内存
~MyString() {
delete[] str; // 释放str指向的数组
}
// 打印字符串
void print() const {
cout << str << endl;
}
};
int main() {
MyString s1("Hello");
MyString s2 = s1; // 调用拷贝构造(深层复制安全)
MyString s3;
s3 = s1; // 调用赋值运算符(深层复制安全)
s1.print(); // Hello
s2.print(); // Hello
s3.print(); // Hello
return 0; // 三个对象析构时释放各自内存,无冲突
}
特性 | 浅层复制 (Shallow Copy) | 深层复制 (Deep Copy) |
|---|---|---|
复制内容 | 仅复制指针值(内存地址) | 复制指针指向的实际数据 |
资源开销 | 低(不分配新内存) | 高(需要分配新内存) |
对象独立性 | 共享资源,修改相互影响 | 完全独立,修改互不影响 |
析构安全性 | 高风险(double free) | 安全(各自管理资源) |
实现方式 | 编译器默认生成 | 需要自定义复制构造/赋值运算符 |
典型应用场景 | 无资源管理的简单对象 | 含指针/文件句柄/网 |
C++ 中有两种字符串处理方式:C 风格字符串(字符数组)和 C++ 风格string类(更安全便捷)。
C 风格字符串是以 '\0'(空字符)结尾的字符数组,需使用<cstring>库函数操作。
strlen(s):计算字符串长度(不含 '\0')strcpy(dst, src):复制字符串(需保证 dst 足够大)strcat(dst, src):拼接字符串strcmp(s1, s2):比较字符串(相等返回 0,s1>s2 返回正)#include <iostream>
#include <cstring>
using namespace std;
int main() {
// 字符数组初始化字符串(自动添加'\0')
char str1[] = "Hello"; // 大小6:'H','e','l','l','o','\0'
char str2[20]; // 预留足够空间
// 字符串长度
cout << "str1长度:" << strlen(str1) << endl; // 输出5
cout << "str1数组大小:" << sizeof(str1) << endl; // 输出6
// 复制字符串
strcpy(str2, str1); // str2 = "Hello"
cout << "复制后str2:" << str2 << endl;
// 拼接字符串
strcat(str2, " World"); // str2 = "Hello World"
cout << "拼接后str2:" << str2 << endl;
// 比较字符串
char str3[] = "Hello";
if(strcmp(str1, str3) == 0){ // 不能用==比较
cout << "str1与str3相等" << endl;
}
return 0;
}
string类是 C++ 标准库提供的字符串类,封装了字符数组,支持直接用运算符操作(如+拼接、==比较),自动管理内存,比 C 风格字符串更安全易用。
#include <iostream>
#include <string> // 需包含头文件
using namespace std;
int main() {
// string初始化
string s1 = "Hello";
string s2("World");
string s3; // 空字符串
// 字符串拼接(+运算符)
s3 = s1 + " " + s2; // s3 = "Hello World"
cout << "s3:" << s3 << endl;
// 字符串长度
cout << "s3长度:" << s3.size() << endl; // 输出11
cout << "s3长度:" << s3.length() << endl; // 输出11(与size等价)
// 字符串比较(==, !=, <, >等)
if(s1 == "Hello"){
cout << "s1等于\"Hello\"" << endl;
}
if(s1 < s2){ // 按字典序比较
cout << "s1在s2前面" << endl; // "Hello" < "World"
}
// 访问字符
cout << "s3第1个字符:" << s3[0] << endl; // 'H'
cout << "s3第6个字符:" << s3.at(5) << endl; // ' '(at会检查越界)
// 字符串截取(substr(pos, len))
string sub = s3.substr(6, 5); // 从位置6开始,取5个字符
cout << "截取子串:" << sub << endl; // 输出"World"
// C风格字符串转换
const char* cstr = s3.c_str(); // 返回const char*
cout << "C风格字符串:" << cstr << endl;
return 0;
}
结合数组、指针、字符串和类,实现一个简单的银行账户管理系统,支持添加账户、查询余额、存款、取款功能。
#include <iostream>
#include <string>
#include <vector> // 用vector存储账户
using namespace std;
// 银行账户类
class BankAccount {
private:
string accountId; // 账号
string name; // 姓名
double balance; // 余额
public:
// 构造函数
BankAccount(string id, string n, double bal = 0.0)
: accountId(id), name(n), balance(bal) {}
// 存款
void deposit(double amount) {
if(amount > 0) {
balance += amount;
cout << "存款成功!当前余额:" << balance << endl;
} else {
cout << "存款金额必须为正数!" << endl;
}
}
// 取款
void withdraw(double amount) {
if(amount <= 0) {
cout << "取款金额必须为正数!" << endl;
return;
}
if(amount > balance) {
cout << "余额不足!当前余额:" << balance << endl;
return;
}
balance -= amount;
cout << "取款成功!当前余额:" << balance << endl;
}
// 查询余额
double getBalance() const {
return balance;
}
// 获取账号
string getAccountId() const {
return accountId;
}
// 显示账户信息
void showInfo() const {
cout << "账号:" << accountId << endl;
cout << "姓名:" << name << endl;
cout << "余额:" << balance << endl;
}
};
// 账户管理类
class AccountManager {
private:
vector<BankAccount> accounts; // 用vector存储账户对象
public:
// 添加账户
void addAccount(const BankAccount& acc) {
accounts.push_back(acc);
cout << "账户添加成功!" << endl;
}
// 根据账号查找账户(返回指针,找不到返回nullptr)
BankAccount* findAccount(const string& id) {
for(auto& acc : accounts) { // 范围for遍历
if(acc.getAccountId() == id) {
return &acc; // 返回找到的账户指针
}
}
return nullptr;
}
// 显示所有账户
void showAllAccounts() const {
if(accounts.empty()) {
cout << "暂无账户!" << endl;
return;
}
cout << "\n===== 所有账户信息 =====" << endl;
for(size_t i=0; i<accounts.size(); i++) { // size_t是无符号整数
cout << "\n账户" << i+1 << ":" << endl;
accounts[i].showInfo();
}
}
};
// 主函数:菜单交互
int main() {
AccountManager manager;
int choice;
do {
// 菜单
cout << "\n===== 银行账户管理系统 =====" << endl;
cout << "1. 添加账户" << endl;
cout << "2. 查询余额" << endl;
cout << "3. 存款" << endl;
cout << "4. 取款" << endl;
cout << "5. 显示所有账户" << endl;
cout << "0. 退出" << endl;
cout << "请选择操作:";
cin >> choice;
switch(choice) {
case 1: { // 添加账户
string id, name;
double bal;
cout << "请输入账号:";
cin >> id;
cout << "请输入姓名:";
cin >> name;
cout << "请输入初始余额:";
cin >> bal;
manager.addAccount(BankAccount(id, name, bal));
break;
}
case 2: { // 查询余额
string id;
cout << "请输入账号:";
cin >> id;
BankAccount* acc = manager.findAccount(id);
if(acc) {
cout << "账户余额:" << acc->getBalance() << endl;
} else {
cout << "账号不存在!" << endl;
}
break;
}
case 3: { // 存款
string id;
double amount;
cout << "请输入账号:";
cin >> id;
BankAccount* acc = manager.findAccount(id);
if(acc) {
cout << "请输入存款金额:";
cin >> amount;
acc->deposit(amount);
} else {
cout << "账号不存在!" << endl;
}
break;
}
case 4: { // 取款
string id;
double amount;
cout << "请输入账号:";
cin >> id;
BankAccount* acc = manager.findAccount(id);
if(acc) {
cout << "请输入取款金额:";
cin >> amount;
acc->withdraw(amount);
} else {
cout << "账号不存在!" << endl;
}
break;
}
case 5: // 显示所有账户
manager.showAllAccounts();
break;
case 0: // 退出
cout << "谢谢使用,再见!" << endl;
break;
default:
cout << "无效选择,请重试!" << endl;
}
} while(choice != 0);
return 0;
}运行示例:
===== 银行账户管理系统 =====
1. 添加账户
2. 查询余额
3. 存款
4. 取款
5. 显示所有账户
0. 退出
请选择操作:1
请输入账号:1001
请输入姓名:张三
请输入初始余额:1000
账户添加成功!
...(省略其他操作步骤)...
===== 所有账户信息 =====
账户1:
账号:1001
姓名:张三
余额:1500引用(int& ref = a;)是变量的别名,与指针有相似之处,但本质不同:
特性 | 指针 | 引用 |
|---|---|---|
定义 | int* p = &a; | int& ref = a; |
初始化 | 可先声明后赋值(如int* p; p=&a;) | 必须初始化(int& ref;错误) |
指向性 | 可指向不同变量(p=&b;) | 一旦绑定,不可更改引用对象 |
空值 | 可指向 nullptr | 必须指向有效变量,无空引用 |
解引用 | 需要*运算符(*p = 10;) | 直接使用(ref = 10;) |
sizeof | 存储地址大小(如 4 或 8 字节) | 引用对象的大小(sizeof(ref)=sizeof(a)) |
作为参数 | 传地址,可修改实参 | 传引用,效果同指针(更简洁) |
#include <iostream>
using namespace std;
// 指针作为参数
void swapPtr(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// 引用作为参数
void swapRef(int& a, int& b) {
int temp = a;
a = b;
b = temp; // 直接操作引用,等价于操作原变量
}
int main() {
int x = 10, y = 20;
// 指针使用
int* p = &x;
*p = 15; // x变为15
p = &y;
*p = 25; // y变为25
cout << "x=" << x << ", y=" << y << endl; // 15,25
// 引用使用
int& ref = x;
ref = 30; // x变为30
// ref = y; // 不是更改引用对象,而是x=y(x变为25)
cout << "x=" << x << ", y=" << y << endl; // 25,25
// 交换函数对比
swapPtr(&x, &y);
cout << "swapPtr后:x=" << x << ", y=" << y << endl; // 25,25(值相同,交换无变化)
x=10, y=20; // 重置
swapRef(x, y);
cout << "swapRef后:x=" << x << ", y=" << y << endl; // 20,10
return 0;
}
指针使用不当会导致严重问题,常见隐患及解决方案:
int* p = nullptr;)*p = 10;当 p=nullptr 时)
解决方案:解引用前检查(if(p != nullptr) *p = 10;)delete p; *p=10;)
解决方案:释放内存后将指针置空(delete p; p=nullptr;)new后无delete)
解决方案:确保new与delete配对,使用智能指针(unique_ptr, shared_ptr)int arr[3]; arr[5] = 10;)
解决方案:访问前检查下标范围,使用 vector 的at()方法#include <iostream>
#include <memory> // 智能指针头文件
using namespace std;
int main() {
// unique_ptr:独占所有权,自动释放内存
unique_ptr<int> up(new int(100)); // 无需手动delete
cout << *up << endl; // 100
// unique_ptr<int> up2 = up; // 错误:unique_ptr不可复制
// shared_ptr:共享所有权,引用计数为0时释放
shared_ptr<string> sp1(new string("Hello"));
cout << "sp1引用计数:" << sp1.use_count() << endl; // 1
shared_ptr<string> sp2 = sp1; // 共享所有权
cout << "sp1引用计数:" << sp1.use_count() << endl; // 2
cout << "sp2指向内容:" << *sp2 << endl; // Hello
sp1.reset(); // 释放sp1的所有权
cout << "sp2引用计数:" << sp2.use_count() << endl; // 1
// 离开作用域时,sp2自动释放内存,无泄漏
return 0;
}
const_cast是 C++ 的类型转换运算符,用于移除指针或引用的 const 属性(仅能转换 const 限定符)。
#include <iostream>
using namespace std;
// 非const函数,修改参数
void modify(int* p) {
*p = 100;
}
int main() {
const int a = 5; // const变量
// int* p = &a; // 错误:不能将const int*转为int*
// 使用const_cast移除const属性
int* p = const_cast<int*>(&a); // 允许转换,但修改const变量是未定义行为!
// 危险:修改原const变量(可能编译器优化导致结果不符合预期)
*p = 10;
cout << "a的值:" << a << endl; // 可能输出5(编译器优化)或10(未优化)
cout << "*p的值:" << *p << endl; // 可能输出10
// 正确用法:临时移除const,不修改原对象
const int b = 20;
const int* cp = &b;
int* np = const_cast<int*>(cp);
// *np = 30; // 禁止:不要修改原const对象!
// 合法场景:非const引用指向const对象(不修改)
const string s = "Hello";
string& ref = const_cast<string&>(s);
// ref = "World"; // 禁止修改!
cout << ref << endl; // 合法:只读访问
return 0;
}
⚠️ 注意:
const_cast不能修改原 const 对象的值,否则行为未定义(可能崩溃或结果异常),仅用于临时移除 const 属性以适配接口。
本章我们学习了 C++ 中三个核心概念:
string类,string类更安全便捷,推荐优先使用。 此外,我们还学习了动态内存分配(new/delete)、vector 动态数组、深层 / 浅层复制、指针与引用的区别等重要知识。这些内容是后续学习数据结构(如链表、树)和高级 C++ 特性的基础,建议多通过实例练习巩固。
关键原则:
new与delete配对使用string和vector,减少原生指针和数组的使用风险希望本章内容对你有所帮助!如果有任何问题或建议,欢迎在评论区留言交流~ 👍</think># 《C++ 程序设计》第 6 章 - 数组、指针与字符串:从基础到实战