1、资料清单
资料
内容
入口
教材
《C++语言程序设计(第4版)》Chap6,原书页码 204-266
抽取 Markdown 下载
课件
Chap6 课件,位于本地郑莉 C++ 资料目录
本地资料夹
例题源代码
C++V5 源代码
下载
VS2019 工程
C++V5 VS2019 solution
下载
本章是 C++ 基础里最容易出错的一章。主线是数组下标 → 指针和地址 → 动态内存 → vector/string → 深拷贝与浅拷贝 。
2、颜色标注
颜色
含义
使用位置
蓝色
核心术语、定义
数组、指针、引用、字符串
绿色
推荐写法、主线
std::vector、std::string、RAII
黄色
易错点、边界条件
下标越界、空指针、浅拷贝
红色
不建议做法
未初始化指针、忘记 delete[]、gets
3、学习目标
目标
要会到什么程度
数组
会定义一维、二维、多维数组,知道下标从 0 开始
指针
分清地址、指针变量、解引用、取地址
引用
能说清引用和指针的区别
动态内存
理解 new / delete、new[] / delete[] 配对
vector
会用 std::vector 替代大多数手写动态数组
字符串
分清 C 风格字符串和 std::string
深拷贝
会解释为什么有指针成员时要写复制构造和赋值
函数指针
能看懂普通函数指针和成员函数指针的声明
4、章节目录
小节
内容
本节关键词
6.1
数组
一维数组、二维数组、多维数组、下标
6.2
指针
地址、解引用、空指针、指针运算
6.3
动态内存分配
new、delete、资源释放
6.4
vector
动态数组、边界管理、标准库容器
6.5
深复制与浅复制
指针成员、复制构造、赋值运算
6.6
字符串
字符数组、\0、std::string
6.7
个人银行账户管理程序改进
数组保存对象、批量管理
5、数组 数组是一组类型相同、内存连续、通过下标访问 的数据。
1 int numbers[5 ] = {1 , 2 , 3 , 4 , 5 };
访问:
1 2 std::cout << numbers[0 ] << '\n' ; std::cout << numbers[4 ] << '\n' ;
数组下标从 0 开始。长度为 n 的数组,合法下标是 0 到 n - 1。访问 array[n] 是越界。
5.1 一维数组 1 2 3 4 5 int scores[3 ] = {90 , 85 , 100 };for (int i = 0 ; i < 3 ; ++i) { std::cout << scores[i] << '\n' ; }
5.2 二维数组 二维数组可以理解成表格。
1 2 3 4 int matrix[2 ][3 ] = { {1 , 2 , 3 }, {4 , 5 , 6 } };
访问第 i 行第 j 列:
5.3 数组长度 对同一作用域内的原生数组,可以用:
1 int length = sizeof (numbers) / sizeof (numbers[0 ]);
但数组传入函数后会退化为指针,sizeof 不再得到原数组总大小。
1 2 3 void print (int array[]) { }
所以现代 C++ 更推荐:
1 2 3 4 5 #include <array> #include <vector> std::array<int , 5> fixedNumbers = {1 , 2 , 3 , 4 , 5 }; std::vector<int > dynamicNumbers = {1 , 2 , 3 , 4 , 5 };
6、指针 指针变量保存的是另一个对象的地址。
1 2 int value = 10 ;int * p = &value;
写法
含义
&value
取 value 的地址
int* p
定义一个指向 int 的指针
*p
解引用,访问指针所指向的对象
1 2 *p = 20 ; std::cout << value << '\n' ;
指针必须先指向有效对象,才能解引用。未初始化指针不能直接 *p = ... 。
6.1 空指针 如果暂时没有指向对象,使用 nullptr。
使用前先判断:
1 2 3 if (p != nullptr ) { std::cout << *p << '\n' ; }
6.2 指针和数组 数组名在很多表达式中会退化为指向首元素的指针。
1 2 3 4 5 int a[3 ] = {10 , 20 , 30 };int * p = a;std::cout << *p << '\n' ; std::cout << *(p + 1 ) << '\n' ;
p + 1 移动到下一个 int 元素,不是地址数字简单加 1。
7、引用和指针 引用是对象的别名,定义时必须初始化,之后不能改绑。
1 2 3 int a = 10 ;int & r = a;r = 20 ;
指针是保存地址的变量,可以改指向,也可以为空。
1 2 3 4 int a = 10 ;int b = 20 ;int * p = &a;p = &b;
对比
引用
指针
是否必须初始化
必须
不必须,但必须使用前有效
是否可以为空
不可以
可以是 nullptr
是否可以改绑
不可以
可以
使用形式
r
*p
8、动态内存 动态内存在运行时申请。
1 2 int * p = new int (5 );delete p;
数组动态分配:
1 2 int * array = new int [10 ];delete [] array;
必须配对:
申请
释放
new T
delete
new T[n]
delete[]
手写 new 后忘记 delete 会内存泄漏;用错 delete / delete[] 也会出问题。现代 C++ 优先用标准库容器和智能指针 。
1 2 3 4 5 #include <memory> #include <vector> auto value = std::make_unique <int >(5 );std::vector<int > array (10 ) ;
9、vector 和 string 9.1 std::vector std::vector 是动态数组,大小可以变化,能自动管理内存。
1 2 3 4 #include <vector> std::vector<int > numbers = {1 , 2 , 3 }; numbers.push_back (4 );
遍历:
1 2 3 for (int value : numbers) { std::cout << value << '\n' ; }
9.2 C 风格字符串 C 风格字符串本质是以空字符 '\0' 结尾的字符数组。
内存中大致是:
9.3 std::string 现代 C++ 推荐优先使用 std::string。
1 2 3 4 #include <string> std::string text = "Hello" ; text += ", C++" ;
它会自动管理内存,支持长度查询、拼接、查找等操作。
10、深拷贝与浅拷贝 如果一个类中有指针成员,默认复制往往只是复制地址,这叫浅拷贝。
1 2 3 4 class BadString {private : char * data; };
浅拷贝的问题:
问题
说明
两个对象指向同一块内存
修改一个会影响另一个
析构时重复释放
两个对象都 delete[] data
生命周期混乱
一个对象销毁后另一个变成悬空指针
深拷贝是复制指针所指向的内容,给新对象分配自己的内存。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class MyBuffer {public : MyBuffer (const MyBuffer& other) : size (other.size), data (new int [other.size]) { for (int i = 0 ; i < size; ++i) { data[i] = other.data[i]; } } ~MyBuffer () { delete [] data; } private : int size; int * data; };
复盘重点:类里一旦出现“自己管理资源”的裸指针,就要想到复制构造函数、赋值运算符和析构函数,也就是传统的 Rule of Three。
11、本章代码复盘 下面这个例子把 vector、string、指针和引用串起来:
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> #include <vector> void addScore (std::vector<int >& scores, int score) { scores.push_back (score); } double average (const std::vector<int >& scores) { int total = 0 ; for (int score : scores) { total += score; } return scores.empty () ? 0.0 : static_cast <double >(total) / scores.size (); } int main () { std::vector<int > scores; addScore (scores, 90 ); addScore (scores, 85 ); addScore (scores, 100 ); std::string name = "Rui" ; const std::string* pName = &name; std::cout << *pName << "'s average: " << average (scores) << '\n' ; return 0 ; }
观察点:
观察点
说明
std::vector<int>&
引用传递,函数能修改原 vector
const std::vector<int>&
避免复制,又保证只读
const std::string*
指针指向的字符串不能通过该指针修改
scores.empty()
防止除以 0
12、课后习题答案 以下答案按现代 C++ 重新整理。原答案中的 iostream.h、void main()、gets() 等旧写法已经替换。
6-1 数组 A[10][5][15] 一共有多少个元素 元素个数:
答案:750 个元素。
6-2 在数组 A[20] 中第一个元素和最后一个元素是哪一个 第一个元素是 A[0],最后一个元素是 A[19]。
6-3 用一条语句定义一个有五个元素的整型数组,并依次赋值 1 到 5 1 int integerArray[5 ] = {1 , 2 , 3 , 4 , 5 };
也可以让编译器推断长度:
1 int integerArray[] = {1 , 2 , 3 , 4 , 5 };
6-4 已知数组 oneArray,用一条语句求出元素个数 1 int length = sizeof (oneArray) / sizeof (oneArray[0 ]);
注意:这个写法只适用于 oneArray 仍然是原生数组的作用域内。如果传入函数后退化为指针,就不能这样求长度。
6-5 定义一个 5 × 3 的二维整型数组,并依次赋值 1 到 15 1 2 3 4 5 6 7 int theArray[5 ][3 ] = { {1 , 2 , 3 }, {4 , 5 , 6 }, {7 , 8 , 9 }, {10 , 11 , 12 }, {13 , 14 , 15 } };
6-6 运算符 * 和 & 的作用是什么
运算符
名称
作用
&
取地址运算符
取得对象的内存地址
*
解引用运算符
访问指针所指向的对象
例子:
1 2 3 int value = 10 ;int * p = &value;std::cout << *p << '\n' ;
6-7 什么叫指针?指针中储存的地址和这个地址中的值有什么区别 指针是一种变量,它保存另一个对象的地址。
1 2 int value = 10 ;int * p = &value;
表达式
含义
p
保存的地址
*p
地址里存放的值
&value
value 的地址
6-8 定义整型指针,用 new 分配 10 个整型元素的地址空间 教材写法:
1 2 int * pInteger = new int [10 ];delete [] pInteger;
现代写法更推荐:
1 std::vector<int > values (10 ) ;
如果必须动态分配,也可以:
1 auto values = std::make_unique <int []>(10 );
6-9 字符串 "Hello, world!" 的结束符是什么 结束符是空字符 '\0'。
C 风格字符串会在最后自动保存一个 '\0',用来表示字符串结束。
6-10 定义五个元素的整型数组,输入并显示 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <iostream> int main () { int myArray[5 ]; for (int i = 0 ; i < 5 ; ++i) { std::cout << "Value for myArray[" << i << "]: " ; std::cin >> myArray[i]; } for (int i = 0 ; i < 5 ; ++i) { std::cout << i << ": " << myArray[i] << '\n' ; } return 0 ; }
示例:
1 2 3 4 5 6 7 8 9 10 Value for myArray[0]: 2 Value for myArray[1]: 5 Value for myArray[2]: 7 Value for myArray[3]: 8 Value for myArray[4]: 3 0: 2 1: 5 2: 7 3: 8 4: 3
6-11 引用和指针有何区别?何时只能使用指针而不能使用引用
对比
引用
指针
本质
对象别名
保存地址的变量
是否必须初始化
必须
可以先为空
是否可为空
不可以
可以是 nullptr
是否可改指向
不可以
可以
使用方式
r
*p
当需要“没有指向任何对象”的状态,或者需要在运行过程中改指向时,只能使用指针。
6-12 声明下列指针 1 2 3 float * pFloat;char * pString;customer* prec;
如果 customer 是结构体类型,也可以写成:
6-13 给定 float 类型指针 fp,显示它所指向的值 1 std::cout << "Value == " << *fp << '\n' ;
使用前要保证 fp 已经指向有效的 float 对象。
6-14 定义一个 double 指针,显示指针大小和所指变量大小 1 2 3 4 5 6 7 8 9 10 11 #include <iostream> int main () { double value = 0.0 ; double * counter = &value; std::cout << "Size of pointer == " << sizeof (counter) << '\n' ; std::cout << "Size of addressed value == " << sizeof (*counter) << '\n' ; return 0 ; }
说明:
表达式
含义
sizeof(counter)
指针变量自身占多少字节
sizeof(*counter)
指针指向的 double 对象占多少字节
6-15 const int* p1 和 int* const p2 的区别 1 2 const int * p1; int * const p2 = &value;
写法
能否改指向
能否通过指针改值
const int* p1
可以
不可以
int* const p2
不可以
可以
6-16 定义变量、指针和引用,通过指针改为 10,通过引用改为 5 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> int main () { int a = 0 ; int * p = &a; int & r = a; *p = 10 ; std::cout << a << '\n' ; r = 5 ; std::cout << a << '\n' ; return 0 ; }
输出:
6-17 程序有什么问题 问题代码的核心错误是指针没有初始化就被解引用。
正确写法:
1 2 3 int value = 0 ;int * p = &value;*p = 9 ;
或者:
1 2 int * p = new int (9 );delete p;
6-18 程序有什么问题,请改正 原程序中 Fn1() 申请了动态内存,但返回值是 *p,导致指针丢失,无法释放,产生内存泄漏。
更直接的修正:不需要动态分配。
1 2 3 4 5 6 7 8 9 10 11 #include <iostream> int fn1 () { return 5 ; } int main () { int a = fn1 (); std::cout << "the value of a is: " << a << '\n' ; return 0 ; }
如果题目要求保留动态分配,可以返回指针并释放:
1 2 3 4 5 6 7 8 9 10 11 12 #include <iostream> int * fn1 () { return new int (5 ); } int main () { int * a = fn1 (); std::cout << "the value of a is: " << *a << '\n' ; delete a; return 0 ; }
现代 C++ 写法:
1 2 3 4 5 6 7 8 9 10 11 12 #include <iostream> #include <memory> std::unique_ptr<int > fn1 () { return std::make_unique <int >(5 ); } int main () { auto a = fn1 (); std::cout << "the value of a is: " << *a << '\n' ; return 0 ; }
6-19 声明函数指针和成员函数指针 声明一个参数为 int,返回值为 long 的普通函数指针:
声明类 A 的一个成员函数指针,参数为 int,返回值为 long:
例子:
1 2 3 4 5 6 7 8 class A {public : long f (int value) { return value; } }; long (A::*p_fn2)(int ) = &A::f;
6-20 实现 SimpleCircle,半径用 int* 保存 这题的重点是指针成员和深拷贝。
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 #include <iostream> class SimpleCircle {public : SimpleCircle () : radius (new int (5 )) {} explicit SimpleCircle (int value) : radius(new int(value)) { } SimpleCircle (const SimpleCircle& other) : radius (new int (other.getRadius ())) {} SimpleCircle& operator =(const SimpleCircle& other) { if (this == &other) { return *this ; } *radius = other.getRadius (); return *this ; } ~SimpleCircle () { delete radius; } void setRadius (int value) { if (value >= 0 ) { *radius = value; } } int getRadius () const { return *radius; } double getArea () const { constexpr double pi = 3.14159265358979323846 ; return pi * (*radius) * (*radius); } private : int * radius; }; int main () { SimpleCircle circleOne; SimpleCircle circleTwo (9 ) ; SimpleCircle circleThree = circleTwo; circleTwo.setRadius (12 ); std::cout << "CircleOne radius: " << circleOne.getRadius () << '\n' ; std::cout << "CircleTwo radius: " << circleTwo.getRadius () << '\n' ; std::cout << "CircleThree radius: " << circleThree.getRadius () << '\n' ; return 0 ; }
输出:
1 2 3 CircleOne radius: 5 CircleTwo radius: 12 CircleThree radius: 9
circleThree 仍然是 9,说明复制构造函数做的是深拷贝,不是两个对象共享同一块半径内存。
6-21 编写函数,统计英文句子中字母个数 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 #include <cctype> #include <iostream> #include <string> int countLetters (const std::string& text) { int count = 0 ; for (unsigned char ch : text) { if (std::isalpha (ch)) { ++count; } } return count; } int main () { std::string text; std::cout << "输入一个英语句子:" << std::endl; std::getline (std::cin, text); std::cout << "这个句子里有 " << countLetters (text) << " 个字母。" << std::endl; return 0 ; }
示例:
1 2 3 输入一个英语句子: It is very interesting! 这个句子里有 19 个字母。
6-22 编写函数 index(s, t),返回子串最左位置 使用 std::string 的版本:
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> #include <string> int indexOf (const std::string& s, const std::string& t) { std::size_t position = s.find (t); if (position == std::string::npos) { return -1 ; } return static_cast <int >(position); } int main () { std::string str1; std::string str2; std::cout << "输入一个英语单词:" ; std::cin >> str1; std::cout << "输入另一个英语单词:" ; std::cin >> str2; int position = indexOf (str1, str2); if (position >= 0 ) { std::cout << str2 << " 在 " << str1 << " 中左起第 " << position + 1 << " 个位置。" << std::endl; } else { std::cout << str2 << " 不在 " << str1 << " 中。" << std::endl; } return 0 ; }
如果按题目保留 C 风格字符串,可以这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 int indexOf (const char * s, const char * t) { for (int i = 0 ; s[i] != '\0' ; ++i) { int j = i; int k = 0 ; while (t[k] != '\0' && s[j] == t[k]) { ++j; ++k; } if (t[k] == '\0' ) { return i; } } return -1 ; }
判断是否找到时应该用 position >= 0。如果子串从第 0 个位置开始,position 等于 0,也是成功匹配。
6-23 编写递归函数 reverse(char* s),使字符串倒序 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 #include <cstring> #include <iostream> void reverseRange (char * left, char * right) { if (left >= right) { return ; } char temp = *left; *left = *right; *right = temp; reverseRange (left + 1 , right - 1 ); } void reverse (char * s) { if (s == nullptr || *s == '\0' ) { return ; } reverseRange (s, s + std::strlen (s) - 1 ); } int main () { char text[100 ]; std::cout << "输入一个字符串:" ; std::cin >> text; std::cout << "原字符串为:" << text << '\n' ; reverse (text); std::cout << "倒序反转后为:" << text << '\n' ; return 0 ; }
示例:
1 2 3 输入一个字符串:abcdefghijk 原字符串为:abcdefghijk 倒序反转后为:kjihgfedcba
6-24 输入 8 个成绩,计算平均成绩 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> int main () { constexpr int studentCount = 8 ; std::vector<double > grades (studentCount) ; for (int i = 0 ; i < studentCount; ++i) { std::cout << "Enter grade #" << i + 1 << ": " ; std::cin >> grades[i]; } double total = 0.0 ; for (double grade : grades) { total += grade; } double average = total / studentCount; std::cout << "\nAverage grade: " << average << std::endl; return 0 ; }
示例:
1 2 3 4 5 6 7 8 9 10 Enter grade #1: 86 Enter grade #2: 98 Enter grade #3: 67 Enter grade #4: 80 Enter grade #5: 78 Enter grade #6: 95 Enter grade #7: 78 Enter grade #8: 56 Average grade: 79.75
6-25 设计字符串类 MyString 这题是本章最重要的综合题:构造、析构、复制构造、赋值、拼接、下标访问,本质都围绕“自己管理一段字符数组”。
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 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 #include <cstring> #include <iostream> #include <stdexcept> class MyString {public : MyString () : data (new char [1 ]), length (0 ) { data[0 ] = '\0' ; } MyString (const char * text) { if (text == nullptr ) { length = 0 ; data = new char [1 ]; data[0 ] = '\0' ; return ; } length = std::strlen (text); data = new char [length + 1 ]; std::strcpy (data, text); } MyString (const MyString& other) : data (new char [other.length + 1 ]), length (other.length) { std::strcpy (data, other.data); } MyString& operator =(const MyString& other) { if (this == &other) { return *this ; } char * newData = new char [other.length + 1 ]; std::strcpy (newData, other.data); delete [] data; data = newData; length = other.length; return *this ; } ~MyString () { delete [] data; } char & operator [](std::size_t index) { if (index >= length) { throw std::out_of_range ("MyString index out of range" ); } return data[index]; } char operator [](std::size_t index) const { if (index >= length) { throw std::out_of_range ("MyString index out of range" ); } return data[index]; } MyString operator +(const MyString& other) const { MyString result; delete [] result.data; result.length = length + other.length; result.data = new char [result.length + 1 ]; std::strcpy (result.data, data); std::strcat (result.data, other.data); return result; } MyString& operator +=(const MyString& other) { *this = *this + other; return *this ; } std::size_t getLength () const { return length; } const char * c_str () const { return data; } private : char * data; std::size_t length; }; int main () { MyString s1 ("initial test" ) ; std::cout << "S1:\t" << s1.c_str () << '\n' ; s1 = "Hello World" ; std::cout << "S1:\t" << s1.c_str () << '\n' ; MyString tempTwo ("; nice to be here!" ) ; s1 += tempTwo; std::cout << "tempTwo:\t" << tempTwo.c_str () << '\n' ; std::cout << "S1:\t" << s1.c_str () << '\n' ; std::cout << "S1[4]:\t" << s1[4 ] << '\n' ; s1[4 ] = 'x' ; std::cout << "S1:\t" << s1.c_str () << '\n' ; MyString s2 (" Another MyString" ) ; MyString s3; s3 = s1 + s2; std::cout << "S3:\t" << s3.c_str () << '\n' ; MyString s4; s4 = "Why does this work?" ; std::cout << "S4:\t" << s4.c_str () << '\n' ; return 0 ; }
输出:
1 2 3 4 5 6 7 8 S1: initial test S1: Hello World tempTwo: ; nice to be here! S1: Hello World; nice to be here! S1[4]: o S1: Hellx World; nice to be here! S3: Hellx World; nice to be here! Another MyString S4: Why does this work?
复盘重点:
成员
作用
构造函数
申请字符数组并写入字符串
复制构造函数
深拷贝,给新对象自己的内存
赋值运算符
处理自赋值,释放旧内存,复制新内容
析构函数
释放 data
operator+
返回拼接后的新字符串
operator+=
在当前对象上追加
operator[]
访问指定字符
真实项目中不要自己写字符串类,直接用 std::string。这道题的价值是理解深拷贝、析构和资源管理。
6-26 编写 3 × 3 矩阵转置函数 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> void transpose (int matrix[3 ][3 ]) { for (int i = 0 ; i < 3 ; ++i) { for (int j = 0 ; j < i; ++j) { int temp = matrix[i][j]; matrix[i][j] = matrix[j][i]; matrix[j][i] = temp; } } } void printMatrix (int matrix[3 ][3 ]) { for (int i = 0 ; i < 3 ; ++i) { for (int j = 0 ; j < 3 ; ++j) { std::cout << matrix[i][j] << ' ' ; } std::cout << '\n' ; } } int main () { int data[3 ][3 ]; std::cout << "输入矩阵的元素" << std::endl; for (int i = 0 ; i < 3 ; ++i) { for (int j = 0 ; j < 3 ; ++j) { std::cout << "第 " << i + 1 << " 行第 " << j + 1 << " 个元素为:" ; std::cin >> data[i][j]; } } std::cout << "输入的矩阵为:" << std::endl; printMatrix (data); transpose (data); std::cout << "转置后的矩阵为:" << std::endl; printMatrix (data); return 0 ; }
输入 1 到 9 时,输出:
1 2 3 4 5 6 7 8 输入的矩阵为: 1 2 3 4 5 6 7 8 9 转置后的矩阵为: 1 4 7 2 5 8 3 6 9
6-27 编写矩阵转置函数,矩阵维数由用户输入 用 std::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 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 #include <iostream> #include <vector> int & at (std::vector<int >& matrix, int n, int row, int col) { return matrix[row * n + col]; } void transpose (std::vector<int >& matrix, int n) { for (int i = 0 ; i < n; ++i) { for (int j = 0 ; j < i; ++j) { int temp = at (matrix, n, i, j); at (matrix, n, i, j) = at (matrix, n, j, i); at (matrix, n, j, i) = temp; } } } void printMatrix (const std::vector<int >& matrix, int n) { for (int i = 0 ; i < n; ++i) { for (int j = 0 ; j < n; ++j) { std::cout << matrix[i * n + j] << ' ' ; } std::cout << '\n' ; } } int main () { int n; std::cout << "请输入矩阵的维数:" ; std::cin >> n; std::vector<int > matrix (n * n) ; std::cout << "输入矩阵的元素" << std::endl; for (int i = 0 ; i < n; ++i) { for (int j = 0 ; j < n; ++j) { std::cout << "第 " << i + 1 << " 行第 " << j + 1 << " 个元素为:" ; std::cin >> matrix[i * n + j]; } } std::cout << "输入的矩阵为:" << std::endl; printMatrix (matrix, n); transpose (matrix, n); std::cout << "转置后的矩阵为:" << std::endl; printMatrix (matrix, n); return 0 ; }
这里用 vector 避免了手动 new[] 后忘记 delete[] 的问题。
6-28 定义 Employee 类,包含姓名、街道地址、城市和邮编 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 #include <iostream> #include <string> class Employee {public : Employee (std::string nameValue, std::string streetValue, std::string cityValue, std::string zipValue) : name (nameValue), street (streetValue), city (cityValue), zip (zipValue) {} void changeName (const std::string& newName) { name = newName; } void display () const { std::cout << name << ' ' << street << ' ' << city << ' ' << zip << '\n' ; } private : std::string name; std::string street; std::string city; std::string zip; }; int main () { Employee employee ("张三" , "平安大街 3 号" , "北京" , "100000" ) ; employee.display (); employee.changeName ("李四" ); employee.display (); return 0 ; }
输出:
1 2 张三 平安大街 3 号 北京 100000 李四 平安大街 3 号 北京 100000
13、本章复盘
复盘问题
自查答案
数组下标从几开始
从 0 开始
& 和 * 分别是什么
取地址与解引用
未初始化指针能否解引用
不能
new[] 应该配什么释放
delete[]
引用和指针最大区别是什么
引用必须绑定对象,指针可以为空且可改指向
C 风格字符串结束符是什么
'\0'
为什么 MyString 要写复制构造和赋值
防止浅拷贝导致共享内存和重复释放
真实项目优先用什么
std::vector 和 std::string
下一章 Chap7 会进入继承与派生,开始把多个类组织成层次结构。