《C++程序设计》笔记p7
宏和模板
写一个宏 SWAP(a, b) 交换两个变量的值。
区别:
- 宏只是源码级别的替换,不进行类型检查。
- 模板进行类型检查,必要时报错,更安全。
函数模板
作用:
函数模板是用来编写与类型无关的通用函数。
定义的语法
template <typename 类型名1, typename 类型名2,...>
返回值 函数名(形参列表) {
}
函数模板使用的
函数模板名<实际类型>(实参列表)
说明:
- 类型名常用
T
开头,如:T
、T1
、T2
- 模板是声明不占用存储空间,一般写在头文件中。
- 在使用函数模板时,C++会在编译阶段自动生成对应的函数代码。
- 关键字
typename
可以使用 关键字class
替换(class
是早期版本C++使用的关键字,新版本的建议使用typename
。 - 在使用函数模板是,如果调用时不指定模版类型,编译器会根据实参类型自动尝试推导模板的类型。
- 一个
template <>
类型声明只对一个模版函数有效。
示例
/*
#define SWAP(a, b) { \auto tmp = a; \a = b; \b = tmp; \
}#define MYSWAP(T) \
void myswap(T &a, T & b) { \auto tmp = a; \a = b; \b = tmp; \
}
MYSWAP(int)
MYSWAP(DynamicArray)
MYSWAP(double)
*/template <typename T>
void myswap(T &a, T & b) {auto tmp = a;a = b;b = tmp;
}int main(int argc, char * argv[]) {DynamicArray d1;DynamicArray d2;for (int i = 10; i <= 50; i+= 10)d1.push_back(i);d2.push_back(100);d2.push_back(200);cout << "d1:" << d1 << endl;cout << "d2:" << d2 << endl;int x = 100;int y = 200;myswap<int>(x, y);cout << "x:" << x << endl;cout << "y:" << y << endl;myswap<DynamicArray>(d1, d2);cout << "d1:" << d1 << endl;cout << "d2:" << d2 << endl;double x1 = 3.14;double x2 = 1.8;myswap<double>(x1, x2);cout << "x1:" << x1 << endl;cout << "x2:" << x2 << endl;return 0;
}
示例:
修改冒泡排序的算法,将其修改为函数模板,使其能对 int 类型的数组,double 类型的数组,甚至动态数组DynamicArray
进行排序。
参考代码
#include <stdio.h>
#include <iostream>using namespace std;class RangeError{public:RangeError(int i): error(i) {}int error;
};class DynamicArray {public:// 初始化动态数组DynamicArray():data(NULL), count(0), max_count(5) {}DynamicArray(const DynamicArray&src):data(NULL), count(src.count), max_count(src.max_count) {if (src.data) { // 如果src有数据才分配内存data = new int[max_count];}// 复制数据for (int i = 0; i < count; i++) {data[i] = src.data[i];}}DynamicArray & operator = (const DynamicArray&src) {if (NULL == src.data) { // src为空列表count = src.count; // count值为0return *this;}// src的数据个数少于this的数据个数if (src.count <= max_count) {count = src.count;for (int i = 0; i < count; i++) {data[i] = src.data[i];}return *this;}// src的数据个数多于 this的数据个数if (data)delete [] data;data = new int[src.max_count];count = src.count;max_count = src.max_count;for (int i = 0; i < count; i++) {data[i] = src.data[i];}return *this;}~DynamicArray() {if (data) {delete [] data;}}// 2. 添加数据void push_back(const int & x){// 需要重新分配内存if (data == NULL) {// 一定要分配内存resizeTo(max_count);} else if (count >= max_count) { // 需要扩容resizeTo(max_count * 2);}data[count] = x;count ++;}// 4. 修改第 i 个学生的成绩。pos 从 0 开始int & operator[](int index) {if (index < 0 || index >= count)throw RangeError(index);return data[index];}// 5. 删除某个位置的学生成绩。bool removeAt(int index) {if (index < 0 || index > count)return false;// throw RangeError(index);for (int i = index; i < count-1; i++) {data[i] = data[i+1];}count--;return true;}DynamicArray operator+(const DynamicArray &r) {// 效率优化版DynamicArray tmp;tmp.max_count = count + r.count + 5;tmp.count = count + r.count;tmp.data = new int[tmp.max_count];// 复制每一个整数值int i;for (i = 0; i < count; i++) {//复制this数据tmp.data[i] = data[i];}//复制r的数据for (int j = 0; j < r.count; j++) {tmp.data[i+j] = r.data[j];}// 简化版// DynamicArray tmp(*this);// for (int i = 0; i < r.count; i++) {// tmp.push_back(r[i]);// }return tmp;}DynamicArray &operator+=(const DynamicArray &r) {if (count + r.count > max_count) {// 从新分配内存resizeTo(max_count + r.max_count);}// 开始复制for (int i = 0; i < r.count; i++) {data[count + i] = r.data[i];}count += r.count;// *this = *this + r;return *this;}int size(void) { // 返回大小return count;}void * get_data(void) { // 返回数据的起始地址return data;}bool empty(void) { // 返回数据是否为空return count == 0;}private:// 扩容到指定的容量volumn, 成共扩容返回true,// 否则返回 false;bool resizeTo(int volumn){// 需要重新分配内存int * temp;if (data == NULL) {// 一定要分配内存data = new int[volumn];return true;}if (count > volumn) // 新容量小于旧容量return false;if (max_count >= volumn)return true;// 真正扩容temp = new int[volumn];for (int i = 0; i < count; i++) {temp[i] = data[i]; // 复制旧数据}delete [] data;data = temp;max_count = volumn;return true;}private:int * data; // 用来保存学生的成绩数据的指针int count; // 用来记录学生的个数。int max_count; // 用来记录当前内存能存储的最大数据friend ostream & operator<<(ostream &o, DynamicArray da);
};ostream & operator<<(ostream &o, DynamicArray da) {o << "[";for (int i = 0; i < da.count; i++) {o << da.data[i];if ( i < da.count -1)o << ", ";}o << "]";return o;
}// 冒泡排序(基础算法)
template <typename T>
void bubble_sort(T *arr, int data_count) {
// void bubble_sort(T &arr, int data_count) {// i 表示此处循环将最大数放入的位置。for (int i = data_count - 1; i > 0; i--) {// j 表示正在准备交换的前一个数据的位置。for (int j = 0; j < i; j++) {if (arr[j] > arr[j+1]) {// 成立则交换auto temp = arr[j];arr[j] = arr[j+1];arr[j+1] = temp;}}}
}template<typename T>
// void print_array(T &arr, int data_count) {
void print_array(T *arr, int data_count) {for (int i = 0; i < data_count; i++) {// printf("%d ", arr[i]);cout << arr[i] << ' ';}printf("\n");
}int main(int argc, char * argv[]) {int data[100] = {6, 8, 1, 9, 3, 7, 5};// bubble_sort<int[100]>(data, 7);bubble_sort(data, 7);print_array(data, 7);double data2[] = {3.14, 2.9, 3, 90.2, 7.8};// bubble_sort<double[5]>(data2, 5);bubble_sort(data2, 5);print_array(data2, 5);DynamicArray d1;d1.push_back(6);d1.push_back(8);d1.push_back(1);d1.push_back(9);bubble_sort((int*)d1.get_data(), d1.size());print_array((int*)d1.get_data(), d1.size());
// print_array(d1, d1.size());printf("程序退出\n");
}
升级后,可以升序排序和降序排序的冒泡排序版本
// 排序
#include <stdio.h>
#include <iostream>using namespace std;class RangeError{public:RangeError(int i): error(i) {}int error;
};class DynamicArray {public:// 初始化动态数组DynamicArray():data(NULL), count(0), max_count(5) {}DynamicArray(const DynamicArray&src):data(NULL), count(src.count), max_count(src.max_count) {if (src.data) { // 如果src有数据才分配内存data = new int[max_count];}// 复制数据for (int i = 0; i < count; i++) {data[i] = src.data[i];}}DynamicArray & operator = (const DynamicArray&src) {if (NULL == src.data) { // src为空列表count = src.count; // count值为0return *this;}// src的数据个数少于this的数据个数if (src.count <= max_count) {count = src.count;for (int i = 0; i < count; i++) {data[i] = src.data[i];}return *this;}// src的数据个数多于 this的数据个数if (data)delete [] data;data = new int[src.max_count];count = src.count;max_count = src.max_count;for (int i = 0; i < count; i++) {data[i] = src.data[i];}return *this;}~DynamicArray() {if (data) {delete [] data;}}// 2. 添加数据void push_back(const int & x){// 需要重新分配内存if (data == NULL) {// 一定要分配内存resizeTo(max_count);} else if (count >= max_count) { // 需要扩容resizeTo(max_count * 2);}data[count] = x;count ++;}// 4. 修改第 i 个学生的成绩。pos 从 0 开始int & operator[](int index) {if (index < 0 || index >= count)throw RangeError(index);return data[index];}// 5. 删除某个位置的学生成绩。bool removeAt(int index) {if (index < 0 || index > count)return false;// throw RangeError(index);for (int i = index; i < count-1; i++) {data[i] = data[i+1];}count--;return true;}DynamicArray operator+(const DynamicArray &r) {// 效率优化版DynamicArray tmp;tmp.max_count = count + r.count + 5;tmp.count = count + r.count;tmp.data = new int[tmp.max_count];// 复制每一个整数值int i;for (i = 0; i < count; i++) {//复制this数据tmp.data[i] = data[i];}//复制r的数据for (int j = 0; j < r.count; j++) {tmp.data[i+j] = r.data[j];}// 简化版// DynamicArray tmp(*this);// for (int i = 0; i < r.count; i++) {// tmp.push_back(r[i]);// }return tmp;}DynamicArray &operator+=(const DynamicArray &r) {if (count + r.count > max_count) {// 从新分配内存resizeTo(max_count + r.max_count);}// 开始复制for (int i = 0; i < r.count; i++) {data[count + i] = r.data[i];}count += r.count;// *this = *this + r;return *this;}int size(void) { // 返回大小return count;}void * get_data(void) { // 返回数据的起始地址return data;}bool empty(void) { // 返回数据是否为空return count == 0;}private:// 扩容到指定的容量volumn, 成共扩容返回true,// 否则返回 false;bool resizeTo(int volumn){// 需要重新分配内存int * temp;if (data == NULL) {// 一定要分配内存data = new int[volumn];return true;}if (count > volumn) // 新容量小于旧容量return false;if (max_count >= volumn)return true;// 真正扩容temp = new int[volumn];for (int i = 0; i < count; i++) {temp[i] = data[i]; // 复制旧数据}delete [] data;data = temp;max_count = volumn;return true;}private:int * data; // 用来保存学生的成绩数据的指针int count; // 用来记录学生的个数。int max_count; // 用来记录当前内存能存储的最大数据friend ostream & operator<<(ostream &o, DynamicArray da);
};ostream & operator<<(ostream &o, DynamicArray da) {o << "[";for (int i = 0; i < da.count; i++) {o << da.data[i];if ( i < da.count -1)o << ", ";}o << "]";return o;
}template <typename N>
void myswap(N & a, N &b) {auto temp = a;a = b;b = temp;
}// 冒泡排序(基础算法)
template <typename T>
void bubble_sort(T *arr, int data_count, bool (*comp)(T &, T &) = NULL) {
// void bubble_sort(T &arr, int data_count) {// i 表示此处循环将最大数放入的位置。for (int i = data_count - 1; i > 0; i--) {// j 表示正在准备交换的前一个数据的位置。for (int j = 0; j < i; j++) {if (comp) {if (comp(arr[j], arr[j+1])) {myswap<T>(arr[j], arr[j+1]);}} else {if (arr[j] > arr[j+1]) {myswap<T>(arr[j], arr[j+1]);}}}}
}template<typename T>
// void print_array(T &arr, int data_count) {
void print_array(T *arr, int data_count) {for (int i = 0; i < data_count; i++) {// printf("%d ", arr[i]);cout << arr[i] << ' ';}printf("\n");
}// 前面比后面的大就返回真(升序排序)
bool comp_int_asc(int & front, int & last) {return front > last;
}
// 降序排序)
bool comp_int_desc(int & front, int & last) {return front < last;
}
int main(int argc, char * argv[]) {int data[100] = {6, 8, 1, 9, 3, 7, 5};// bubble_sort<int[100]>(data, 7);bubble_sort(data, 7, &comp_int_desc);print_array(data, 7);double data2[] = {3.14, 2.9, 3, 90.2, 7.8};// bubble_sort<double[5]>(data2, 5);bubble_sort(data2, 5);print_array(data2, 5);DynamicArray d1;d1.push_back(6);d1.push_back(8);d1.push_back(1);d1.push_back(9);bubble_sort((int*)d1.get_data(), d1.size());print_array((int*)d1.get_data(), d1.size());
// print_array(d1, d1.size());printf("程序退出\n");
}
类模板
作用:编写于类型无关的通用类,实现与类型无关代码的复用。
类模板的语法
template <typename 类型参数名1[=缺省类型1], typename 类型参数名2[=缺省类型2],...>
class 类名 {... 类的实现
};template <typename ...>
class 类名2 {
}
说明:
- template 类模板是声明,不是实现,一般放在头文件中(不会生成实际类)。
- 一个template 关键字声明只对一个类或一个函数有效。
typename
可以用class
代替(新版本建议使用typename).
类模板创建类的语法
类模板名<真实存在的类型名> 变量1, 变量2;
示例:
#include <iostream>
// #include <stdio.h>
// #include <stdlib.h>using namespace std;class RangeError{public:RangeError(int i): error(i) {}int error;
};template<typename T=int>
class DynamicArray {public:// 初始化动态数组DynamicArray():data(NULL), count(0), max_count(5) {}DynamicArray(const DynamicArray&src):data(NULL), count(src.count), max_count(src.max_count) {if (src.data) { // 如果src有数据才分配内存data = new T/*int*/[max_count];}// 复制数据for (int i = 0; i < count; i++) {data[i] = src.data[i];}}DynamicArray & operator = (const DynamicArray&src) {if (NULL == src.data) { // src为空列表count = src.count; // count值为0return *this;}// src的数据个数少于this的数据个数if (src.count <= max_count) {count = src.count;for (int i = 0; i < count; i++) {data[i] = src.data[i];}return *this;}// src的数据个数多于 this的数据个数if (data)delete [] data;data = new T/*int*/[src.max_count];count = src.count;max_count = src.max_count;for (int i = 0; i < count; i++) {data[i] = src.data[i];}return *this;}~DynamicArray() {if (data) {delete [] data;}}// 2. 添加数据void push_back(const T/*int*/ & x){// 需要重新分配内存if (data == NULL) {// 一定要分配内存resizeTo(max_count);} else if (count >= max_count) { // 需要扩容resizeTo(max_count * 2);}data[count] = x;count ++;}// 4. 修改第 i 个学生的成绩。pos 从 0 开始T/*int*/ & operator[](int index) {if (index < 0 || index >= count)throw RangeError(index);return data[index];}// 5. 删除某个位置的学生成绩。bool removeAt(int index) {if (index < 0 || index > count)return false;// throw RangeError(index);for (int i = index; i < count-1; i++) {data[i] = data[i+1];}count--;return true;}// 7. 在 pos 之前的位置插入学生的成绩bool insertAt(int pos, T/*int*/ score){}DynamicArray operator+(const DynamicArray &r) {// 效率优化版DynamicArray tmp;tmp.max_count = count + r.count + 5;tmp.count = count + r.count;tmp.data = new T/*int*/ [tmp.max_count];// 复制每一个整数值int i;for (i = 0; i < count; i++) {//复制this数据tmp.data[i] = data[i];}//复制r的数据for (int j = 0; j < r.count; j++) {tmp.data[i+j] = r.data[j];}// 简化版// DynamicArray tmp(*this);// for (int i = 0; i < r.count; i++) {// tmp.push_back(r[i]);// }return tmp;}DynamicArray &operator+=(const DynamicArray &r) {if (data == NULL) {resizeTo(max_count + r.max_count);}if (count + r.count > max_count) {// 从新分配内存resizeTo(max_count + r.max_count);}// 开始复制for (int i = 0; i < r.count; i++) {data[count + i] = r.data[i];}count += r.count;// *this = *this + r;return *this;}//模板内声明int size(void);// int size(void) { // 返回大小// return count;// }void * get_data(void) { // 返回数据的起始地址return data;}bool empty(void) { // 返回数据是否为空return count == 0;}private:// 扩容到指定的容量volumn, 成共扩容返回true,// 否则返回 false;bool resizeTo(int volumn){// 需要重新分配内存T * temp;if (data == NULL) {// 一定要分配内存data = new T/*int*/[volumn];max_count = volumn;return true;}if (count > volumn) // 新容量小于旧容量return false;if (max_count >= volumn)return true;// 真正扩容temp = new T/*int*/[volumn];for (int i = 0; i < count; i++) {temp[i] = data[i]; // 复制旧数据}delete [] data;data = temp;max_count = volumn;return true;}private:T * data; // 用来数据的指针int count; // 用来记录个数。int max_count; // 用来记录当前内存能存储的最大数据template<typename T1>friend ostream & operator<<(ostream &o, DynamicArray<T1> da);
};template <typename T>
int DynamicArray<T>::size(void) { // 返回大小return count;
}template<typename T1>
ostream & operator<<(ostream &o, DynamicArray<T1> da) {o << "[";for (int i = 0; i < da.count; i++) {o << da.data[i];if ( i < da.count -1) o << ", ";}o << "]";return o;
}
int main(int argc, char * argv[]) {// DynamicArray<Shape*> shapes;DynamicArray<int> d1;DynamicArray<int> d3 = d1;for (int i = 10; i <= 50; i+= 10)d1.push_back(i);d3 += d1;DynamicArray<double> d2;cout << "d1:" << d1 << endl;cout << "d2:" << d2 << endl;d2.push_back(10.1);d2.push_back(20.2);cout << "d1:" << d1 << endl;cout << "d2:" << d2 << endl;cout << "d2.size(): " << d2.size() << endl;return 0;
}
函数模板和类模板是泛型算法的基础。
泛型算法
泛型算法是一种编程范式,核心思想是编写与类型无关的通用代码,让算法和数据结构等能够适用于不同的数据类型。
核心思想:
一次编写,多次使用。
模版的参数语法:
模版的参数内不仅可以有模版类型的定义,还可以有变量的定义。
template <typename T, int N>
class 模板名{...
};
示例代码
#include <iostream>
// #include <stdio.h>
// #include <stdlib.h>using namespace std;template<typename T, int N>
class StaticArray{public:T data[N];
};int main(int argc, char * argv[]) {StaticArray<int, 100> a1;return 0;
}
标准模板库(STL)
标准模板库(Standard Template Library) 是C++的基础库,它包含在 std名字空间中。
标准模板库核心组成
- 容器(Container)
- 算法(Algorithms)
- 迭代器(Iterators)
容器Container
- 动态数组 vector
- 定长数组 array
- 双端队列 deque
- 映射 map
- 无序映射 unordered_map
- 集合 set
- 无序集合unordered_set
vector 的常用成员函数
.push_back(const T& value) 向后追加
.pop_back() 删除尾部数据
.at( size_type pos )返回某位置数据的引用(做边界检测)
.operator[]( size_type pos ) 返回某位置数据的引用
.front() 返回第一个数据
.back() 返回最后一个数据
.clear() 清空数组。
.resize(size_type count, const value_type& value),将数组变为指定的长度,如果 count <= .size() 则删除后面的数据,如果count > .size(),多余的位置填充 value,
示例代码:
#include <iostream>
#include <vector>using namespace std;
class Student {public:Student(const string &n, int a, int s):name(n),age(a),score(s){}bool operator<(const Student &other) const {return score < other.score;}string name;int age;int score;
};int main(int argc, char * argv[]) {vector<Student> v1 = {Student("张3", 18, 100)};vector<Student> v2 = {Student("李四", 19, 80)};// vector<int> v1 = {1, 2, 3};// vector<int> v2 = {100, 2, 3};cout << (v1 < v2) << endl;return 0;
}
迭代器
STL的容器类中都封装了迭代器类,用于操作容器类内的数据。
迭代器,可以理解成访问成员的指针。
作用:统一容器类的数据访问方法。便于范型编程。
.begin() // 返回容器数据开始位置的迭代器,类型:iterator
.end() // 返回容器数据结束位置的迭代器(最后一个元素的后面)
.rbegin()//返回反项迭代器的起始位置(最后一个元素),类型reverse_iterator
.rend() // 返回反项迭代器的结束位置(第一个元素的前一位置)。// 返回常迭代器
.cbegin()、.cend(): // 返回类型:const_iterator
.crbegin()、.crend(); // 返回类型:const_reverse_iterator
迭代器大概图示:
示例
#include <iostream>
#include <vector>
#include <chrono>using namespace std;int main(int argc, char * argv[]) {int buf[] = {11, 22, 33, 44, 55};int * begin;int * end;begin = &buf[0];end = &buf[5];int * it; // it 就是遍历数组 buf的迭代器;for (it = begin; it != end; ++it) {// *it *= 2;cout << *it << endl;}// 容器类的迭代器vector<int> myarr = {111, 222, 333, 444};for (vector<int>::iterator it=myarr.begin();it != myarr.end(); ++it) {cout << *it << endl;}const int * cit; // 常迭代器for (cit = begin; cit != end; ++cit) {// *cit *= 2; // 报错cout << *cit << endl;}// for (vector<int>::const_iterator it=myarr.cbegin();for (auto it=myarr.cbegin();it != myarr.cend(); ++it) {// *it *= 2; // 报错cout << *it << endl;}//打印 444, 333, 222, 111for (vector<int>::reverse_iterator it = myarr.rbegin();it != myarr.rend(); ++it) {cout << *it << endl;}vector<int>::iterator myit = myarr.begin();cout << "第一个元素:" << *myit << endl;myit += 2;cout << "第三个元素:" << *myit << endl;return 0;
}
vector 的常用成员函数(用于插入和删除的方法)
// 在指定位置插入数据
iterator insert( const_iterator pos, const T& value );
// 在指定位置插入count 个相同值的数据
iterator insert( const_iterator pos,size_type count, const T& value );
// 删除指定位置的数据
iterator erase( iterator pos );
iterator erase( const_iterator pos );
// 删除指定范围内的数据(不包含last)
iterator erase( iterator first, iterator last );
iterator erase( const_iterator first, const_iterator last );
range_for 语句
用于遍历原生数组或含有.begin() 和 .end()成员函数并返回迭代器的可迭代的对象。
可迭代对象是指能用.begin() 和 .end() 成员函数 返回迭代器的对象。
语法:
for (类型 变量: 可迭代对象) {... 循环体.
}
示例:
for (int x: myarr) {...
}
// 等同于:
for (auto it = myarr.begin(); it != myarr.end(); ++it) {int x = *it;...
}
示例代码
#include <iostream>
#include <vector>using namespace std;
class Student {public:Student(const string &n, int a, int s):name(n),age(a),score(s){}bool operator<(const Student &other) const {return score < other.score;}string name;int age;int score;
};int main(int argc, char * argv[]) {vector<Student> v1 = {Student("张3", 18, 100),Student("李4", 19, 80),Student("王5", 17, 70),};// 遍历容器对象for (const Student &s:v1) {cout << "姓名:" << s.name << ",年龄:" << s.age << endl;}// 遍历原生数组int a1[] = {111, 222, 333, 444};for (int value: a1) {cout << "value:" << value << endl;}return 0;
}
vector 内部还可以包含动态数组
示例代码:
#include <iostream>
#include <vector>using namespace std;
class Student {public:Student(const string &n, int a, int s):name(n),age(a),score(s){}bool operator<(const Student &other) const {return score < other.score;}string name;int age;int score;
};int main(int argc, char * argv[]) {vector<vector<Student>> school;return 0;
}
range_for 的原理(迭代器原理)
参考代码:
#include <iostream>
// #include <stdio.h>
// #include <stdlib.h>using namespace std;class RangeError{public:RangeError(int i): error(i) {}int error;
};class DynamicArray {public:class iterator {public:iterator(DynamicArray & d, int pos = 0): da(d), cur_pos(pos) {}bool operator!=(const iterator & r) const {return cur_pos != r.cur_pos;}iterator & operator++(void) {++cur_pos;return *this;}int & operator*(void) { // 解引用运算符重载return da[cur_pos];}int * operator->(void) {return &da[cur_pos];}private:DynamicArray & da; // 绑定迭代的动态数组int cur_pos; // 使用记录迭代的位置};public:iterator begin() { // 返回开始的迭代器return iterator(*this);}iterator end() { // 返回结束位置后一个元素的迭代器return iterator(*this, count);}public:// 初始化动态数组DynamicArray():data(NULL), count(0), max_count(5) {}DynamicArray(const DynamicArray&src):data(NULL), count(src.count), max_count(src.max_count) {if (src.data) { // 如果src有数据才分配内存data = new int[max_count];}// 复制数据for (int i = 0; i < count; i++) {data[i] = src.data[i];}}DynamicArray & operator = (const DynamicArray&src) {if (NULL == src.data) { // src为空列表count = src.count; // count值为0return *this;}// src的数据个数少于this的数据个数if (src.count <= max_count) {count = src.count;for (int i = 0; i < count; i++) {data[i] = src.data[i];}return *this;}// src的数据个数多于 this的数据个数if (data)delete [] data;data = new int[src.max_count];count = src.count;max_count = src.max_count;for (int i = 0; i < count; i++) {data[i] = src.data[i];}return *this;}~DynamicArray() {if (data) {delete [] data;}}// 2. 添加数据void push_back(const int & x){// 需要重新分配内存if (data == NULL) {// 一定要分配内存resizeTo(max_count);} else if (count >= max_count) { // 需要扩容resizeTo(max_count * 2);}data[count] = x;count ++;}// 4. 修改第 i 个学生的成绩。pos 从 0 开始int & operator[](int index) {if (index < 0 || index >= count)throw RangeError(index);return data[index];}// 5. 删除某个位置的学生成绩。bool removeAt(int index) {if (index < 0 || index > count)return false;// throw RangeError(index);for (int i = index; i < count-1; i++) {data[i] = data[i+1];}count--;return true;}DynamicArray operator+(const DynamicArray &r) {// 效率优化版DynamicArray tmp;tmp.max_count = count + r.count + 5;tmp.count = count + r.count;tmp.data = new int[tmp.max_count];// 复制每一个整数值int i;for (i = 0; i < count; i++) {//复制this数据tmp.data[i] = data[i];}//复制r的数据for (int j = 0; j < r.count; j++) {tmp.data[i+j] = r.data[j];}// 简化版// DynamicArray tmp(*this);// for (int i = 0; i < r.count; i++) {// tmp.push_back(r[i]);// }return tmp;}DynamicArray &operator+=(const DynamicArray &r) {if (data == NULL) {resizeTo(max_count + r.max_count);}if (count + r.count > max_count) {// 从新分配内存resizeTo(max_count + r.max_count);}// 开始复制for (int i = 0; i < r.count; i++) {data[count + i] = r.data[i];}count += r.count;// *this = *this + r;return *this;}int size(void) { // 返回大小return count;}void * get_data(void) { // 返回数据的起始地址return data;}bool empty(void) { // 返回数据是否为空return count == 0;}private:// 扩容到指定的容量volumn, 成共扩容返回true,// 否则返回 false;bool resizeTo(int volumn){// 需要重新分配内存int * temp;if (data == NULL) {// 一定要分配内存data = new int[volumn];return true;}if (count > volumn) // 新容量小于旧容量return false;if (max_count >= volumn)return true;// 真正扩容temp = new int[volumn];for (int i = 0; i < count; i++) {temp[i] = data[i]; // 复制旧数据}delete [] data;data = temp;max_count = volumn;return true;}private:int * data; // 用来保存学生的成绩数据的指针int count; // 用来记录学生的个数。int max_count; // 用来记录当前内存能存储的最大数据friend ostream & operator<<(ostream &o, DynamicArray da);
};ostream & operator<<(ostream &o, DynamicArray da) {o << "[";for (int i = 0; i < da.count; i++) {o << da.data[i];if ( i < da.count -1) o << ", ";}o << "]";return o;
}
int main(int argc, char * argv[]) {DynamicArray d1;for (int i = 10; i <= 50; i+= 10)d1.push_back(i);// for (DynamicArray::iterator it = d1.begin(); // it != d1.end(); ++it) {// int & x = *it;// cout << x << endl;// }for (auto & x: d1) {cout << x << endl;}return 0;
}
双向链表 list
头文件
#include <list>
名字空间: std
list 的常用方法
.size() 返回列表内数据的长度。empty() 返回链表内数据是否为空。
。max_size() 返回链表等容纳数据的最大容量(依赖系统和环境)。
。clear() 清空列表
.insert(iterator pos, const T & value) 插入数据.
.erase(iterator pos) 删除数据.
.push_back(const T & value) 在末尾插入数据.
.push_front(const T & value) 在头部插入数据.
.pop_back() 删除末尾数据.
.pop_front() 删除开头数据.
map 映射表和 unordered_map 无序映射表
特点
- 以键值对的形式存储数据,根据键查找值的速度快
- map 查找时间复杂度
O(Log2n)O(Log_2^n)O(Log2n)
,使用二叉排序树存储。 - unordered_map查找时间复杂度
O(1)O(1)O(1)
,使用哈希表存储。 - map、unordered_map键不能重复。
- multimap和 unordered_multimap 键可以重复。
map 常用的方法
.find( const Key& key ); 根据键查找,返回迭代器iterator,没找到 iterator == map.end()。
示例
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <unordered_map>using namespace std;
class Student {public:Student(const string &n="", int a=0, int s=0):name(n),age(a),score(s){}bool operator<(const Student &other) const {return score < other.score;}string name;int age;int score;
};int main(int argc, char * argv[]) {//键(key)是字符串,表示姓名,值(value) 是整数类型表示年龄// std::map<string, int> m;unordered_map<string, int> m;m["weimingze"] = 35;m["zhang3"] = 18;m["li4"] = 19; // 插入数据(键值对);cout << "映射表的数据个数是:" << m.size() << endl;m["li4"] = 88; // 修改(键对应的值);m.insert({string("wang5"), 12});cout << m["王五"] << endl;cout << "映射表的数据个数是:" << m.size() << endl;cout << "李四的年龄是:" << m["li4"] << endl;for (auto it = m.begin();it != m.end(); ++it) {cout << "键:" << it->first<< " 值:" << it->second << endl;}// for (auto & pair: m) {// cout << "键:" << pair.first<< " 值:" << pair.second << endl;// }// map<int, Student> person;// person[100] = Student("张3", 18, 100);// cout << "person的数据个数:" << person.size() << endl;// vector<Student> v1 = {
// Student("张3", 18, 100),
// Student("李4", 19, 80),
// Student("王5", 17, 70),
// };return 0;
}
集合set
和无序集合unordered_set
特点:
- 数据不能重复,查找速度快。
- set 查找时间复杂度
O(Log2n)O(Log_2^n)O(Log2n)
,使用二叉排序树存储。 - unordered_set查找时间复杂度
O(1)O(1)O(1)
,使用哈希表存储。 - set、unordered_set值不能重复。
- multiset和 unordered_multiset 值可以重复。
示例
#include <iostream>
#include <set>using namespace std;int main(int argc, char * argv[]) {std::set<string> s; // 创建一个集合s.insert("zhang3"); // 插入数据s.insert("li4");s.insert("zhang3");s.insert("wang5");s.insert("li4");cout << s.size() << endl; // 集合默认去重复return 0;
}