前言 本篇blog是读者所总结(cv大法均有包含出处,笔者水平有限,如果错误请指出(主要期末考总结了一下方便考前再次复习
理论基础 在protected保护继承中,对于垂直访问等同于公有继承,对于水平访问等同于私有继承。
动态绑定是在运行时选定调用的成员函数的。
对于从基类继承的虚函数,派生类也可以不进行重定义。
类A是类B的友元,说明类A是友元类
友元不能传递也不能继承:破坏封装性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class ClassB ;class ClassA {public : void display (const ClassB& b) ; }; class ClassB { friend class ClassA ; private : int privateData = 10 ; protected : int protectedData = 20 ; }; void ClassA::display (const ClassB& b) { std::cout << "Private Data: " << b.privateData << std::endl; std::cout << "Protected Data: " << b.protectedData << std::endl; }
在销毁派生类对象时,先调用基类的析构函数,再调用派生类的析构函数
new new
出来的对象仅仅是调用对象的构造函数,new之后要显示的调用delete函数
palcement new
的主要用途就是反复使用一块较大的动态分配的内存来构造不同类型的对象或者他们的数组。placement new构造起来的对象或其数组,要显示的调用他们的析构函数 来销毁,千万不要使用delete。
static修饰的对象时,只有在程序结束时才会调用析构函数,位于main函数中定义的对象之后
友元 友元的关键字为 friend
友元的三种实现
类做友元
成员函数做友元
全局函数做友元
friend修饰别的类
friend修饰成员函数
friend修饰类外定义的函数
链表 链表确实是一种重要的动态数据结构,它在许多情况下非常有用,但您的描述中有几点需要澄清和修正:
动态内存分配 : 链表确实根据需要动态地开辟内存空间。每个新元素(通常称为节点)都是在需要时创建的,这使得链表在内存使用方面非常灵活。
插入和删除的灵活性 : 链表可以在几乎任何位置轻松地插入或删除节点。由于不需要像数组那样移动元素,这些操作通常很高效,尤其是当你可以直接访问到要操作的节点的前一个节点时。
不支持随机访问 : 链表不支持随机访问 。数组支持高效的随机访问,即可以直接通过索引在常数时间内访问任何元素。而链表则需要从头开始遍历,直到到达所需的元素,这使得访问特定元素是线性时间的操作。
内存和操作效率 :
内存使用 :相比于数组,链表的内存使用通常更高,因为每个节点不仅要存储数据,还需要存储至少一个指向列表中下一个节点的指针。在双向链表中,还需要存储指向前一个节点的指针。
操作效率 :链表的操作效率依赖于具体操作。对于在链表头部或已知位置插入和删除操作,链表非常高效。然而,对于需要搜索特定元素的操作,链表的效率通常低于数组,因为需要遍历链表元素。
节省内存 : 链表不一定能节省内存,特别是当节点包含的数据较小时,额外的指针所需的内存可能会使得链表的总内存占用实际上高于同等数量的数组元素。
运算符重载 在C++中,大多数运算符可以被重载为成员函数或非成员函数(包括友元函数),但并非所有运算符都可以用这三种方式重载。以下是一些关键点和例外:
成员函数和非成员函数
成员函数 :当一个运算符被重载为成员函数时,它的第一个操作数必须是调用该成员函数的对象本身。这意味着,对于二元运算符,左操作数是对象本身,右操作数是作为参数传递的。
非成员函数 :这些通常被实现为普通函数或友元函数。友元函数虽然定义在类的外部,但它可以访问类的所有私有和保护成员。
友元函数
友元函数 :不是类的成员,但有权访问类的私有和保护成员。友元函数通常用于那些需要访问两个不同类对象的私有数据的运算符重载。
特殊运算符重载
赋值运算符(=
) :只能作为成员函数重载。这是因为赋值运算符需要改变对象自身的状态,且左侧操作数必须是类类型的对象。
下标运算符([]
) 、函数调用运算符(()
) 和箭头运算符(->
) :也只能作为成员函数重载。
递增(++
) 和递减(--
)运算符 :可以作为成员函数或非成员函数重载,但通常作为成员函数重载,以便可以直接修改对象的状态。
示例 举个例子,考虑一个简单的类Vector
,我们可以重载加法运算符+
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class Vector {public : int x, y; Vector operator +(const Vector& other) { return {x + other.x, y + other.y}; } friend Vector operator +(const Vector& lhs, const Vector& rhs) { return {lhs.x + rhs.x, lhs.y + rhs.y}; } };
在这个例子中,加法运算符可以作为成员函数或非成员友元函数重载。选择哪种方式取决于具体需求,例如,如果需要访问两个不同对象的私有成员,则可能需要使用友元函数。
结论 虽然很多运算符可以以不同方式重载,但并非所有运算符都可以通过所有三种方式(成员、非成员、友元)重载。选择合适的重载方式取决于特定的需求和设计目标。
实践实例 vector使用 下面是一些vector
的用法示例:
**创建和初始化vector
**:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <vector> #include <iostream> int main () { std::vector<int > numbers; std::vector<int > numbers = {1 , 2 , 3 , 4 , 5 }; std::vector<int > numbers; numbers.push_back (1 ); numbers.push_back (2 ); numbers.push_back (3 ); std::vector<int > numbers (5 , 0 ) ; return 0 ; }
访问和修改vector
中的元素 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <vector> #include <iostream> int main () { std::vector<int > numbers = {1 , 2 , 3 , 4 , 5 }; std::cout << numbers[0 ] << std::endl; std::cout << numbers.at (2 ) << std::endl; numbers[3 ] = 10 ; 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 23 #include <vector> #include <iostream> int main () { std::vector<int > numbers = {1 , 2 , 3 , 4 , 5 }; std::cout << "Size: " << numbers.size () << std::endl; for (std::vector<int >::iterator it = numbers.begin (); it != numbers.end (); ++it) { std::cout << *it << " " ; } std::cout << std::endl; for (int num : numbers) { std::cout << num << " " ; } std::cout << std::endl; return 0 ; }
插入和删除元素 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <vector> #include <iostream> int main () { std::vector<int > numbers = {1 , 2 , 3 , 4 , 5 }; numbers.insert (numbers.begin () + 2 , 10 ); numbers.push_back (6 ); numbers.erase (numbers.begin () + 1 ); numbers.pop_back (); 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 #include <iostream> #include <string> #include <string.h> #include <vector> using namespace std ; int main () { std ::vector <int > numbers = { 1 , 2 , 3 , 4 , 5 }; numbers.insert(numbers.begin() + 2 , 10 ); numbers.push_back(6 ); numbers.erase(numbers.begin() + 1 ); numbers.pop_back(); for (int num : numbers) { std ::cout << num << " " ; } std ::cout << std ::endl ; return 0 ; }
公倍数以及公约数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <iostream> int gcd (int a, int b) { if (b == 0 ) { return a; } return gcd(b, a % b); } int lcm (int a, int b) { return a / gcd(a, b) * b; }
重载格式 在C++中,运算符重载是一种允许为已有的运算符提供用户定义的实现的语言特性。这使得开发者可以对自定义数据类型使用标准运算符。下面提供了一些常见运算符的重载格式,包括成员函数和非成员函数(包括友元函数)的重载方式。
1. 加法运算符(+
) 成员函数形式 :
1 2 3 4 5 6 7 class MyClass {public : MyClass operator +(const MyClass& rhs) const { return MyClass (); } };
非成员(友元)函数形式 :
1 2 3 4 5 6 7 class MyClass {public : friend MyClass operator +(const MyClass& lhs, const MyClass& rhs) { return MyClass (); } };
2. 赋值运算符(=
) 成员函数形式 (只能是成员函数):
1 2 3 4 5 6 7 class MyClass {public : MyClass& operator =(const MyClass& rhs) { return *this ; } };
3. 下标运算符([]
) 成员函数形式 (只能是成员函数):
1 2 3 4 5 6 7 class MyClass {public : ElementType& operator [](int index) { return elements[index]; } };
4. 递增运算符(++
) 前缀递增 :
1 2 3 4 5 6 7 class MyClass {public : MyClass& operator ++() { return *this ; } };
后缀递增 :
1 2 3 4 5 6 7 8 class MyClass {public : MyClass operator ++(int ) { MyClass temp = *this ; return temp; } };
5. 输出运算符(<<
)常用于输出流重载 非成员(友元)函数形式 :
1 2 3 4 5 6 7 class MyClass {public : friend std::ostream& operator <<(std::ostream& os, const MyClass& obj) { return os; } };
6. 比较运算符(==
) 成员函数形式 :
1 2 3 4 5 6 7 class MyClass {public : bool operator ==(const MyClass& rhs) const { return ; } };
非成员(友元)函数形式 :
1 2 3 4 5 6 7 class MyClass {public : friend bool operator ==(const MyClass& lhs, const MyClass& rhs) { return ; } };
这些示例展示了如何重载常见运算符。在实际使用中,选择成员函数还是友元函数形式,通常取决于是否需要访问私有成员,以及是否希望第一个操作数是类类型的对象。总的来说,运算符重载应该谨慎使用,以保持代码的直观和易于理解。
问题 为什么选择 int
作为参数?(后置++)
语法规定 :C++语言规范定义了后缀递增运算符的重载必须使用一个int
类型的参数。这个参数的存在是为了语法上的需要,以便编译器能够区分后缀递增(operator++(int)
)和前缀递增(operator++()
)。
占位符用途 :这个int
参数实际上是一个占位符,它不需要被赋予任何实际的值。在调用后缀递增时,通常传递的是一个字面值0,但这个值在实际的运算符实现中通常是被忽略的。
为什么MyClass&是&? 在C++中,使用引用返回类型(例如 MyClass&
)在运算符重载和其他方法中是一种常见的做法,尤其是在赋值运算符和前缀递增/递减运算符中。这样做有几个重要的原因和优势:
1. 避免不必要的对象复制 当函数返回类型是一个对象而不是引用时,C++标准通常要求返回值被复制或移动到调用方的变量中。如果返回类型是一个引用,这种复制可以被避免。对于包含大量数据或复杂资源管理的类来说,避免这种复制是提高性能的关键。
2. 允许链式调用 返回对象的引用允许方法调用可以被链式连接起来。这是因为返回的引用指向调用对象本身,所以可以继续在同一个表达式中对其进行操作。例如:
1 myObject.Increment ().SetSomething (5 ).DoAnotherThing ();
在这个示例中,Increment
、SetSomething
和 DoAnotherThing
都可能返回 MyClass&
,允许连续调用。
3. 保持操作符的预期行为 对于某些操作符,如赋值 (=
)、前缀递增 (++
) 和前缀递减 (--
),按照惯例和预期,这些操作应该修改原始对象,并返回修改后的对象的引用。这样,操作的结果可以直接用于其他操作,模仿了内置类型的行为。例如:
在这个表达式中,第一个 ++
操作修改了 myObject
并返回了它的引用,第二个 ++
立即作用于同一个对象。
4. 实现符合直觉的语义 通过返回引用,你确保了操作符或方法的行为符合使用者的直觉。例如,赋值运算符通常预期能直接在赋值后使用对象,如在表达式或条件中:
在这里,a = b
赋值并测试与 c
是否相等,这种行为是通过返回 a
的引用来实现的。
总结 使用 MyClass&
作为返回类型提供了性能优势,允许链式调用,保持了操作符的预期行为,并实现了符合直觉的语义。这些都是在设计类接口时考虑使用引用作为返回类型的重要因素。
抽象类and接口 以下是一个简单的抽象类示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class Shape {public : virtual void draw () const = 0 ; virtual double area () const = 0 ; }; class Circle : public Shape {private : double radius; public : Circle (double r) : radius (r) {} void draw () const override { std::cout << "Drawing a circle." << std::endl; } double area () const override { return 3.14159 * radius * radius; } };
类的引用重写 在C++中,如果类A公有地继承自类B,那么类B的指针或引用可以指向类A的对象,但反过来不行。这是因为类B只是类A的一部分,而类A包含了类B的所有特性以及额外的特性。让我们通过一个例子来更清楚地解释这一点。
示例说明 假设我们有一个基类 Base
和一个从 Base
公有继承的派生类 Derived
:
1 2 3 4 5 6 7 8 9 10 11 12 13 class Base {public : void baseMethod () { std::cout << "Base method" << std::endl; } }; class Derived : public Base {public : void derivedMethod () { std::cout << "Derived method" << std::endl; } };
在这种情况下,我们可以使用 Derived
类的对象来初始化 Base
类的引用或指针,因为每个 Derived
对象都是一个 Base
对象。这是多态的基础,允许 Base
类型的引用或指针调用在 Derived
类中重写的方法。
1 2 3 Derived derivedObj; Base &baseRef = derivedObj; baseRef.baseMethod ();
然而,反过来不行:
1 2 3 Base baseObj; Derived &derivedRef = baseObj; derivedRef.derivedMethod ();
这里的错误发生是因为 baseObj
只是一个 Base
类型的对象,它可能没有 Derived
类中定义的额外成员和方法。尝试将一个 Base
类型的对象的引用转换为 Derived
类型的引用是不安全的,因为 Derived
可能有更多的数据成员或方法,这在 Base
对象中并不存在。
结论 因此,公有继承允许基类的指针或引用指向派生类的对象,但不能使用基类对象来初始化派生类的引用。这种类型的引用或指针转换是单向的,只能从派生类到基类,而不是反过来。这是面向对象设计中的一个重要安全特性,它保护了程序的类型安全性。
algorithm头文件 #include <algorithm>
是C++标准库中的一个头文件,它提供了大量的函数模板,用于处理各种算法操作,包括排序、搜索、合并、替换、旋转、反转等。以下是一些常用的函数和它们的用途:
排序 :
sort(first, last)
:对范围内的元素进行排序。
stable_sort(first, last)
:对范围内的元素进行稳定排序。
搜索 :
find(first, last, value)
:在范围内查找特定值的第一个出现位置。
binary_search(first, last, value)
:在已排序的范围内执行二分查找。
合并 :
merge(first1, last1, first2, last2, result)
:合并两个已排序的范围到一个新的范围。
替换 :
replace(first, last, old_value, new_value)
:将范围内的所有旧值替换为新值。
旋转 :
rotate(first, middle, last)
:将范围内的元素旋转,使得中间元素成为新的第一个元素。
反转 :
reverse(first, last)
:反转范围内元素的顺序。
补充:
result.back()是result的末尾
result.pop_back();移除最后一位
stoi(result);转换为int型(老pwn了)
result.substr(7)将提取从指定位置开始到字符串末尾的所有字符
result.substr(7,5)从指定位置开始读取5个字符
在使用 <algorithm>
头文件中的函数时,对类型的要求主要涉及以下几个方面:
迭代器要求 :
大多数算法函数需要至少是输入迭代器(Input Iterator)的迭代器类型。这意味着你可以使用这些函数来处理任何支持输入迭代器的容器,如std::vector
、std::list
、std::deque
、std::array
等,以及普通数组。
某些算法可能需要更高级的迭代器类型,如前向迭代器(Forward Iterator)、双向迭代器(Bidirectional Iterator)或随机访问迭代器(Random Access Iterator)。例如,sort()
函数通常需要随机访问迭代器。
等等(还有很多的要求主要涉及自定义类在此不过多赘述
algorithm
头文件是C++标准库中的一个重要组成部分,它提供了大量的算法,用于处理和操作容器(如数组、std::vector
、std::list
、std::set
、std::map
等)中的元素。以下是一些 algorithm
头文件中常用函数的使用示例:
std::sort
:对容器中的元素进行排序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <algorithm> #include <vector> #include <iostream> int main () { std::vector<int > v = {3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 }; std::sort (v.begin (), v.end ()); for (int i : v) { std::cout << i << ' ' ; } std::cout << std::endl; return 0 ; }
std::find
:在容器中查找特定元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <algorithm> #include <vector> #include <iostream> int main () { std::vector<int > v = {1 , 2 , 3 , 4 , 5 }; int target = 3 ; auto it = std::find (v.begin (), v.end (), target); if (it != v.end ()) { std::cout << "Element found at position: " << (it - v.begin ()) << std::endl; } else { std::cout << "Element not found" << std::endl; } return 0 ; }
std::reverse
:反转容器中的元素顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <algorithm> #include <vector> #include <iostream> int main () { std::vector<int > v = {1 , 2 , 3 , 4 , 5 }; std::reverse (v.begin (), v.end ()); for (int i : v) { std::cout << i << ' ' ; } std::cout << std::endl; return 0 ; }
std::max_element
和 std::min_element
:找到容器中的最大和最小元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <algorithm> #include <vector> #include <iostream> int main () { std::vector<int > v = {3 , 1 , 4 , 1 , 5 , 9 , 2 , 6 }; auto max_it = std::max_element (v.begin (), v.end ()); auto min_it = std::min_element (v.begin (), v.end ()); std::cout << "Max element: " << *max_it << std::endl; std::cout << "Min element: " << *min_it << std::endl; return 0 ; }
这些只是 algorithm
头文件中提供的众多算法的一小部分。使用这些算法可以大大简化对容器中元素的操作,提高代码的效率和可读性。
链表 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 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 C++单链表的操作 2017 -12 -25 #include "stdafx.h" #include <iostream> using namespace std ; typedef int DataType; #define Node ElemType #define ERROR NULL class Node { public: int data; Node * next; }; class LinkList { public: LinkList(); ~LinkList(); void CreateLinkList (int n) ; void TravalLinkList () ; int GetLength () ; bool IsEmpty () ; ElemType * Find (DataType data) ; void InsertElemAtEnd (DataType data) ; void InsertElemAtIndex (DataType data,int n) ; void InsertElemAtHead (DataType data) ; void DeleteElemAtEnd () ; void DeleteAll () ; void DeleteElemAtPoint (DataType data) ; void DeleteElemAtHead () ; private: ElemType * head; }; LinkList::LinkList() { head = new ElemType; head->data = 0 ; head->next = NULL ; } LinkList::~LinkList() { delete head; } void LinkList::CreateLinkList (int n) { ElemType *pnew, *ptemp; ptemp = head; if (n < 0 ) { cout << "输入的节点个数有误" << endl ; exit (EXIT_FAILURE); } for (int i = 0 ; i < n;i++) { pnew = new ElemType; cout << "请输入第" << i + 1 << "个值: " ; cin >> pnew->data; pnew->next = NULL ; ptemp->next = pnew; ptemp = pnew; } } void LinkList::TravalLinkList () { if (head == NULL || head->next ==NULL ) { cout << "链表为空表" << endl ; } ElemType *p = head; while (p->next != NULL ) { p = p->next; cout << p->data << " " ; } } int LinkList::GetLength () { int count = 0 ; ElemType *p = head->next; while (p != NULL ) { count++; p = p->next; } return count; } bool LinkList::IsEmpty () { if (head->next == NULL ) { return true ; } return false ; } ElemType * LinkList::Find (DataType data) { ElemType * p = head; if (p == NULL ) { cout << "此链表为空链表" << endl ; return ERROR; } else { while (p->next != NULL ) { if (p->data == data) { return p; } p = p->next; } return NULL ; } } void LinkList::InsertElemAtEnd (DataType data) { ElemType * newNode = new ElemType; newNode->next = NULL ; newNode->data = data; ElemType * p = head; if (head == NULL ) { head = newNode; } else { while (p->next != NULL ) { p = p->next; } p->next = newNode; } } void LinkList::InsertElemAtIndex (DataType data,int n) { if (n<1 || n>GetLength()) cout << "输入的值错误" << endl ; else { ElemType * ptemp = new ElemType; ptemp->data = data; ElemType * p = head; int i = 1 ; while (n > i) { p = p->next; i++; } ptemp->next = p->next; p->next = ptemp; } } void LinkList::InsertElemAtHead (DataType data) { ElemType * newNode = new ElemType; newNode->data = data; ElemType * p = head; if (head == NULL ) { head = newNode; } newNode->next = p->next; p->next = newNode; } void LinkList::DeleteElemAtEnd () { ElemType * p = head; ElemType * ptemp = NULL ; if (p->next == NULL ) { cout << "单链表空" << endl ; } else { while (p->next != NULL ) { ptemp = p; p = p->next; } delete p; p = NULL ; ptemp->next = NULL ; } } void LinkList::DeleteAll () { ElemType * p = head->next; ElemType * ptemp = new ElemType; while (p != NULL ) { ptemp = p; p = p->next; head->next = p; ptemp->next = NULL ; delete ptemp; } head->next = NULL ; } void LinkList::DeleteElemAtPoint (DataType data) { ElemType * ptemp = Find(data); if (ptemp == head->next) { DeleteElemAtHead(); } else { ElemType * p = head; while (p->next != ptemp) { p = p->next; } p->next = ptemp->next; delete ptemp; ptemp = NULL ; } } void LinkList::DeleteElemAtHead () { ElemType * p = head; if (p == NULL || p->next == NULL ) { cout << "该链表为空表" << endl ; } else { ElemType * ptemp = NULL ; p = p->next; ptemp = p->next; delete p; p = NULL ; head->next = ptemp; } } int main () { LinkList l; int i; cout << "1.创建单链表 2.遍历单链表 3.获取单链表的长度 4.判断单链表是否为空 5.获取节点\n" ; cout << "6.在尾部插入指定元素 7.在指定位置插入指定元素 8.在头部插入指定元素\n" ; cout <<"9.在尾部删除元素 10.删除所有元素 11.删除指定元素 12.在头部删除元素 0.退出" << endl ; do { cout << "请输入要执行的操作: " ; cin >> i; switch (i) { case 1 : int n; cout << "请输入单链表的长度: " ; cin >> n; l.CreateLinkList(n); break ; case 2 : l.TravalLinkList(); break ; case 3 : cout << "该单链表的长度为" << l.GetLength() << endl ; break ; case 4 : if (l.IsEmpty() == 1 ) cout << "该单链表是空表" << endl ; else { cout << "该单链表不是空表" << endl ; } break ; case 5 : DataType data; cout << "请输入要获取节点的值: " ; cin >> data; cout << "该节点的值为" << l.Find(data)->data << endl ; break ; case 6 : DataType endData; cout << "请输入要在尾部插入的值: " ; cin >> endData; l.InsertElemAtEnd(endData); break ; case 7 : DataType pointData; int index; cout << "请输入要插入的数据: " ; cin >> pointData; cout << "请输入要插入数据的位置: " ; cin >> index; l.InsertElemAtIndex(pointData, index); break ; case 8 : DataType headData; cout << "请输入要在头部插入的值: " ; cin >> headData; l.InsertElemAtHead(headData); break ; case 9 : l.DeleteElemAtEnd(); break ; case 10 : l.DeleteAll(); break ; case 11 : DataType pointDeleteData; cout << "请输入要删除的数据: " ; cin >> pointDeleteData; l.DeleteElemAtPoint(pointDeleteData); break ; case 12 : l.DeleteElemAtHead(); break ; default : break ; } }while (i != 0 ); system("pause" ); return 0 ; }
模板 在C++中,模板是一种强大的特性,它允许你编写与类型无关的代码。模板可以用于函数(函数模板)和类(类模板)。下面是一些函数模板和类模板的示例。
函数模板示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <iostream> template <typename T>void swapValues (T& a, T& b) { T temp = a; a = b; b = temp; } int main () { int a = 5 , b = 10 ; std::cout << "Before swap: a = " << a << ", b = " << b << std::endl; swapValues (a, b); std::cout << "After swap: a = " << a << ", b = " << b << std::endl; double x = 3.14 , y = 6.28 ; std::cout << "Before swap: x = " << x << ", y = " << y << std::endl; swapValues (x, y); std::cout << "After swap: x = " << x << ", y = " << y << std::endl; return 0 ; }
在这个示例中,swapValues
是一个函数模板,它可以用来交换任何类型的两个值。
类模板示例 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 #include <iostream> #include <vector> #include <algorithm> template <typename T>class Printer {private : std::vector<T> elements; public : void addElement (const T& element) { elements.push_back (element); } void printElements () { for (const auto & element : elements) { std::cout << element << " " ; } std::cout << std::endl; } }; int main () { Printer<int > intPrinter; intPrinter.addElement (1 ); intPrinter.addElement (2 ); intPrinter.addElement (3 ); intPrinter.printElements (); Printer<std::string> stringPrinter; stringPrinter.addElement ("Hello" ); stringPrinter.addElement ("World" ); stringPrinter.printElements (); return 0 ; }
在这个示例中,Printer
是一个类模板,它可以用来存储和打印任何类型的元素。
模板特化示例 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 #include <iostream> template <typename T>T max (T a, T b) { std::cout << "General template" << std::endl; return (a > b) ? a : b; } template <>const char * max (const char * a, const char * b) { std::cout << "Specialized template" << std::endl; return (std::strcmp (a, b) > 0 ) ? a : b; } int main () { int a = 5 , b = 10 ; std::cout << "Max of " << a << " and " << b << " is " << max (a, b) << std::endl; const char * x = "apple" ; const char * y = "banana" ; std::cout << "Max of " << x << " and " << y << " is " << max (x, y) << std::endl; return 0 ; }
在这个示例中,我们为 max
函数模板提供了一个特化版本,专门用于比较 const char*
类型的字符串。
这些示例展示了C++模板的基本用法,包括函数模板、类模板和模板特化。模板是C++中实现泛型编程的关键特性,它允许你编写高度可重用的代码。