📘 C++ std::string
函数全表(附原型和头文件)
✅ 所有内容基于 C++11~C++20 标准,适用于复习准备考试和写项目时查阅。
🔹 基本信息函数
函数名称 |
原型 |
返回值 |
头文件 |
说明 |
length / size |
size_t size() const; |
size_t |
<string> |
返回字符串长度 |
empty |
bool empty() const; |
bool |
<string> |
判断是否为空 |
clear |
void clear(); |
void |
<string> |
清空字符串内容 |
resize |
void resize(size_t n); void resize(size_t n, char c); |
void |
<string> |
调整大小,必要时用 c 补充 |
capacity |
size_t capacity() const; |
size_t |
<string> |
当前容量(分配的空间) |
reserve |
void reserve(size_t n); |
void |
<string> |
预留容量 |
shrink_to_fit |
void shrink_to_fit(); |
void |
<string> |
缩小容量至正好容纳内容 |
max_size |
size_t max_size() const; |
size_t |
<string> |
最大可保存字符数 |
🔹 元素访问
函数名称 |
原型 |
返回值 |
说明 |
operator[] |
char& operator[](size_t pos); const char& operator[](size_t pos) const; |
char& / const char& |
访问字符(无越界检查) |
at |
char& at(size_t pos); const char& at(size_t pos) const; |
char& / const char& |
访问字符(有越界检查) |
front |
char& front(); const char& front() const; |
char& / const char& |
返回首字符 |
back |
char& back(); const char& back() const; |
char& / const char& |
返回末字符 |
data |
const char* data() const noexcept; |
const char* |
返回内部数据指针 |
c_str |
const char* c_str() const noexcept; |
const char* |
返回C风格字符串 |
🔹 修改操作
函数名称 |
原型 |
返回值 |
说明 |
operator+= |
string& operator+=(const string& str); string& operator+=(const char* s); string& operator+=(char c); |
string& |
追加字符串/字符 |
append |
string& append(const string& str); string& append(const char* s); |
string& |
追加内容 |
insert |
string& insert(size_t pos, const string& str); |
string& |
插入字符串 |
erase |
string& erase(size_t pos = 0, size_t len = npos); |
string& |
删除部分内容 |
replace |
string& replace(size_t pos, size_t len, const string& str); |
string& |
替换内容 |
push_back |
void push_back(char c); |
void |
尾部添加字符 |
pop_back |
void pop_back(); |
void |
删除尾字符 |
assign |
多个重载 |
string& |
赋值为某个字符串/子串 |
swap |
void swap(string& other); |
void |
交换两个字符串 |
🔹 查找相关
函数名称 |
原型 |
返回值 |
说明 |
find |
size_t find(const string& str, size_t pos = 0) const; |
size_t |
查找子串首次出现位置 |
rfind |
size_t rfind(const string& str, size_t pos = npos) const; |
size_t |
逆向查找 |
find_first_of |
size_t find_first_of(const string& str, size_t pos = 0) const; |
size_t |
查找任意字符首次出现位置 |
find_last_of |
size_t find_last_of(const string& str, size_t pos = npos) const; |
size_t |
查找任意字符最后出现位置 |
find_first_not_of |
size_t find_first_not_of(const string& str, size_t pos = 0) const; |
size_t |
第一次出现不属于某集合的位置 |
find_last_not_of |
size_t find_last_not_of(const string& str, size_t pos = npos) const; |
size_t |
最后一次出现不属于集合的位置 |
substr |
string substr(size_t pos = 0, size_t len = npos) const; |
string |
获取子串 |
🔹 比较函数
函数名称 |
原型 |
返回值 |
说明 |
compare |
int compare(const string& str) const; |
int |
字符串比较(<0, 0, >0) |
== != < > <= >= |
重载操作符 |
bool |
支持直接比较字符串 |
🔹 输入输出(非成员函数)
函数名称 |
原型 |
返回值 |
说明 |
operator>> |
istream& operator>>(istream& is, string& str); |
istream& |
输入(遇空格停止) |
getline |
istream& getline(istream& is, string& str); |
istream& |
输入一整行 |
operator<< |
ostream& operator<<(ostream& os, const string& str); |
ostream& |
输出字符串 |
🔹 C++11+ 新增函数(若支持)
函数名称 |
原型 |
返回值 |
说明 |
to_string(val) |
string to_string(int val); 等 |
string |
数值转字符串 |
stoi , stol , stof , stod , 等 |
int stoi(const string& str); 等 |
数值类型 |
字符串转数值 |
starts_with(str) |
bool starts_with(const string& str) const; |
bool |
(C++20)判断前缀 |
ends_with(str) |
bool ends_with(const string& str) const; |
bool |
(C++20)判断后缀 |
🧠 附录:重要常量
名称 |
类型 |
含义 |
string::npos |
size_t |
表示查找失败,通常为 -1 的无符号值 |
📘 C++ STL 常用函数速查笔记(附原型、返回值、适用容器)
标准头文件:<vector>
, <list>
, <deque>
, <stack>
, <queue>
, <map>
, <unordered_map>
, <set>
, <algorithm>
, <iterator>
, <utility>
等
模板头文件统一建议加:#include <bits/stdc++.h>
(竞赛常用)或按需包含
Matrix不让用万能头(
🔹 1. 容器通用函数(所有序列容器几乎都适用)
函数 |
原型 |
返回值类型 |
说明 |
size() |
size_t size() const; |
size_t |
返回容器元素数量 |
empty() |
bool empty() const; |
bool |
是否为空 |
clear() |
void clear(); |
void |
清空所有元素 |
insert(pos, val) |
不同容器不同重载 |
迭代器或 void |
插入元素 |
erase(pos) |
不同重载 |
迭代器或 void |
删除元素 |
swap(cont) |
void swap(Container& other); |
void |
与另一个容器交换内容 |
begin() / end() |
iterator |
迭代器 |
返回迭代器首/尾 |
rbegin() / rend() |
reverse_iterator |
迭代器 |
反向迭代器 |
front() |
T& front(); |
元素类型引用 |
返回第一个元素 |
back() |
T& back(); |
元素类型引用 |
返回最后一个元素 |
find(val) |
适用于 set/map/unordered_map |
迭代器 |
查找某值是否存在(注意区分) |
count(val) |
size_t count(const Key& key) const; |
size_t |
统计某值出现次数(多用于 multiset/multimap) |
🔹 2. vector<T>
头文件:#include <vector>
函数 |
原型 |
返回值类型 |
说明 |
push_back(val) |
void push_back(const T& val); |
void |
添加元素到末尾 |
pop_back() |
void pop_back(); |
void |
删除最后一个元素 |
resize(n) |
void resize(size_t n); |
void |
改变大小 |
reserve(n) |
void reserve(size_t n); |
void |
预留容量 |
operator[] |
T& operator[](size_t); |
T& |
随机访问元素 |
at(n) |
T& at(size_t); |
T& |
带越界检查访问元素 |
🔹 3. list<T>
(双向链表)
头文件:#include <list>
函数 |
原型 |
返回值类型 |
说明 |
push_front(val) |
void push_front(const T& val); |
void |
前插 |
push_back(val) |
void push_back(const T& val); |
void |
后插 |
pop_front() |
void pop_front(); |
void |
删除第一个元素 |
pop_back() |
void pop_back(); |
void |
删除最后一个元素 |
remove(val) |
void remove(const T& val); |
void |
删除所有等于 val 的元素 |
unique() |
void unique(); |
void |
删除连续重复元素 |
reverse() |
void reverse(); |
void |
反转链表 |
sort() |
void sort(); |
void |
排序(内部自带) |
🔹 4. deque<T>
头文件:#include <deque>
与 list
类似,支持下标访问,支持双端插入和删除:
push_front
, push_back
pop_front
, pop_back
operator[]
, at()
🔹 5. stack<T>
头文件:#include <stack>
函数 |
原型 |
返回值类型 |
说明 |
push(val) |
void push(const T& val); |
void |
入栈 |
pop() |
void pop(); |
void |
出栈 |
top() |
T& top(); |
元素类型引用 |
返回栈顶元素 |
empty() |
bool empty() const; |
bool |
是否为空 |
size() |
size_t size() const; |
size_t |
元素个数 |
🔹 6. queue<T>
和 priority_queue<T>
头文件:#include <queue>
queue
push(val)
pop()
front()
back()
priority_queue(默认大顶堆)
top()
获取最大值
- 自定义小顶堆:
priority_queue<int, vector<int>, greater<int>> pq;
🔹 7. set<T>
/ multiset<T>
/ unordered_set<T>
头文件:<set>
, <unordered_set>
函数 |
说明 |
insert(val) |
插入元素(自动有序,或哈希存储) |
erase(val) |
删除指定元素(返回个数) |
find(val) |
查找并返回迭代器 |
count(val) |
出现次数(set=0或1,multiset可能>1) |
lower_bound(val) |
第一个不小于 val 的迭代器 |
upper_bound(val) |
第一个大于 val 的迭代器 |
🔹 8. map<Key, Value>
/ unordered_map<Key, Value>
头文件:<map>
, <unordered_map>
函数 |
原型 |
返回值 |
insert({key, val}) |
插入键值对 |
pair<iterator, bool> |
erase(key) |
删除键 |
size_t |
find(key) |
返回迭代器 |
iterator |
count(key) |
是否存在 |
0/1 |
operator[] |
Value& operator[](const Key& key) |
若不存在则自动插入 |
at(key) |
const Value& at(const Key& key) const; |
查找键对应值,越界会报错 |
🔹 9. 算法 <algorithm>
函数 |
功能 |
sort(begin, end) |
快速排序 |
reverse(begin, end) |
翻转 |
find(begin, end, val) |
查找 |
count(begin, end, val) |
计数 |
lower_bound(begin, end, val) |
二分第一个 ≥ val |
upper_bound(begin, end, val) |
二分第一个 > val |
binary_search(...) |
是否存在 |
next_permutation(...) |
下一个全排列 |
max_element(...) / min_element(...) |
最大 / 最小元素位置 |
📎 可选 STL 容器简明总结
容器 |
特点 |
是否排序 |
支持随机访问 |
vector |
动态数组 |
❌ |
✅ |
list |
双向链表 |
❌ |
❌ |
deque |
双端队列 |
❌ |
✅ |
set |
自动有序不重复 |
✅ |
❌ |
map |
键值对,自动排序 |
✅ |
❌ |
unordered_map |
哈希键值对 |
❌ |
❌ |
stack |
栈结构(封装) |
❌ |
❌ |
queue |
队列结构(封装) |
❌ |
❌ |
系统回顾你在学习 C++ 类与对象时遇到的各种语法问题,尤其是关于 ::
的使用问题。这类问题通常和作用域解析、类外定义、静态成员、嵌套类等密切相关。
🧠 一、你常见的问题总结
你在之前学习过程中常遇到以下类型的错误或疑惑(我根据上下文记忆进行总结):
✅ 1. ClassName::
作用域解析符误用 / 不清楚使用场景
问题 |
原因 |
❌ 在类内定义成员函数时使用 ClassName:: |
类内定义时不能加 ClassName:: |
❌ 在类外定义时忘记加 ClassName:: |
类外定义必须加 |
❌ 把 :: 写在对象前面,比如 obj::func() |
:: 不能用于对象名,只用于类名或命名空间 |
❌ 不理解 :: 与 . 或 -> 的区别 |
混淆了“类作用域解析”与“对象成员访问” |
✅ 2. 静态成员函数和静态成员变量的访问混乱
问题 |
原因 |
❌ 静态成员函数中访问非静态成员变量 |
静态成员函数没有 this 指针 |
❌ 静态变量只声明不定义 |
静态成员变量必须类外定义一次:Type ClassName::var = ...; |
❌ 使用对象访问静态成员导致歧义 |
推荐用 ClassName:: 来访问静态成员 |
✅ 3. 类外定义函数语法错误
常见出错格式:
1 2 3 4 5 6 7 8 9 10 11
| class A { public: void func(); };
void func() { ... }
void A::func() { ... }
|
✅ 4. 嵌套作用域或多重命名空间时 ::
语法混乱
场景 |
举例 |
命名空间嵌套 |
A::B::func() |
类中类 |
Outer::Inner::func() |
类中 enum / typedef / static const |
ClassName::EnumName |
全局作用域访问 |
::var 表示全局变量,防止和局部变量重名 |
📌 二、重点回顾 ::
(作用域解析运算符)
📍用法 1:类外定义成员函数
1 2 3 4 5
| class A { public: void show(); }; void A::show() { cout << "Hello"; }
|
📍用法 2:访问静态成员
1 2 3 4 5
| class A { public: static int x; }; int A::x = 100;
|
📍用法 3:命名空间中的函数或变量
1 2 3 4 5
| namespace myns { int x = 5; void func(); } void myns::func() { cout << x; }
|
📍用法 4:嵌套类或作用域引用
1 2 3 4 5 6 7 8
| class Outer { public: class Inner { public: void hello(); }; }; void Outer::Inner::hello() { cout << "Hi"; }
|
📍用法 5:访问全局变量(避免局部变量遮蔽)
1 2 3 4 5
| int x = 10; void func() { int x = 20; cout << ::x; }
|
✅ 三、你可能遇到的真实错误例子(回顾性质)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class A { void show(); }; void show() { ... }
class A { static int val; }; int val = 10;
class A { void func() { A::val = 10; } int val; };
|
类继承易错点
🔶 一、基本继承语法回顾(快速过)
1 2 3 4 5 6 7 8 9 10
| class Base { public: void show(); protected: int value; };
class Derived : public Base { };
|
- ✅
public
继承:最常用,基类public和protected成员在子类中可访问
- ✅
protected
继承:基类public变成protected
- ✅
private
继承:基类所有成员都变成private
🧠 二、常见继承结构中的易错点与例子
📍【易错点1】子类访问权限混乱
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| class A { protected: int val = 42; };
class B : private A { public: void access() { cout << val; } };
class C : public B { public: void tryAccess() { cout << val; } };
|
🛠 易错点:派生类继承链中对访问权限传播判断错误
📍【易错点2】父类构造函数初始化写法错误
1 2 3 4 5 6 7 8 9
| class A { public: A(int x) { cout << x; } };
class B : public A { public: B() : A(10) {} };
|
🛠 易错点:
- 写成
B() { A(10); }
(没用初始化列表)
- 忘记手动调用参数构造
🔷 三、重点:菱形继承(Diamond Inheritance)
🔻 问题来源:两个子类继承同一个父类,然后又有子类继承它们
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class A { public: int val; };
class B : public A { }; class C : public A { };
class D : public B, public C { public: void access() { val = 5; } };
|
🛠 易错点:成员二义性(两个 A::val)
✅ 正确做法:虚继承(virtual)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class A { public: int val; };
class B : virtual public A {}; class C : virtual public A {};
class D : public B, public C { public: void access() { val = 10; } };
|
🛠 易错点:
- 忘记加
virtual
,会产生两份 A
(造成资源浪费和访问歧义)
- 构造函数初始化 A 要由最下层子类负责
1 2 3 4 5 6 7 8 9 10 11 12
| class A { public: A(int x) { cout << x; } }; class B : virtual public A { public: B() : A(0) {} }; class D : public B { public: D() : A(10), B() {} };
|
📌 菱形继承结构图(文字版)
不加 virtual
⇒ 会产生两份 A
加了 virtual
⇒ 共用一份 A
,统一由 D
初始化
🔷 四、其他复杂继承模式中的易错点
📍【易错点3】多重继承中成员名冲突
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class A { public: int id = 1; };
class B { public: int id = 2; };
class C : public A, public B { public: void print() { cout << id; cout << A::id; } };
|
🛠 易错点:不明确指定作用域,编译器无法判断
📍【易错点4】函数重写但忘记加 virtual
1 2 3 4 5 6 7 8 9
| class A { public: void show() { cout << "A"; } };
class B : public A { public: void show() { cout << "B"; } };
|
🛠 建议:凡是你想子类重写的函数,都要 virtual
或 override
📍【易错点5】多继承中虚函数覆盖混乱
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class Base1 { public: virtual void f() { cout << "Base1"; } };
class Base2 { public: virtual void f() { cout << "Base2"; } };
class Derived : public Base1, public Base2 { public: void f() { cout << "Derived"; } };
|
🛠 问题:你需要手动指定 Base1::f()
或 Base2::f()
否则指针调用不明确:
1 2
| Derived d; Base1* p1 = &d; p1->f();
|
✅ 总结:继承中最常出错的地方
类别 |
易错点 |
:: |
成员函数类外定义、静态变量定义写错作用域 |
普通继承 |
忘记用初始化列表构造父类、权限传播判断错误 |
多继承 |
成员函数/变量重名未显式指明、虚函数表冲突 |
菱形继承 |
忘记 virtual 导致二义性、构造顺序错误 |
虚函数 |
没加 virtual ,导致多态失败 |
成员访问 |
用对象名+:: 访问成员,语法错误 |
复杂类层次 |
忘记最底层子类负责初始化虚基类 |
运算符重载
类内定义
非常好,运算符重载确实是 C++ 类设计中的重点与难点,你过去在这个主题上也遇到不少典型的语法问题。下面我会回顾你过往交流中的常见错误点,并给出系统的正确写法总结,涵盖:
- 成员函数 vs. 友元函数重载区别
- 二元/一元运算符正确写法
- 赋值、比较、输入输出运算符的完整范式
- 容易忘写/错写的点(如
const
、返回引用、传值 vs. 引用)
🧠 一、你曾经常错/混淆的地方
错误类型 |
常见表现 |
❌ 成员 vs. 非成员不清楚 |
把 << , + 写成成员函数 |
❌ 返回类型错误 |
operator= 不返回引用;operator++ 不区分前后缀 |
❌ 没加 const |
比较运算符、<< 、+ 结果被误修改 |
❌ 参数类型错误 |
传值导致性能问题;引用传参但参数没加 const |
❌ 成员函数+友元函数混用出错 |
写了友元,但没写在类外定义 |
✅ 二、标准运算符重载写法汇总
1️⃣ +
:加法运算符
1 2 3 4 5 6 7 8 9 10
| class MyClass { int val; public: MyClass(int v = 0) : val(v) {}
MyClass operator+(const MyClass& rhs) const { return MyClass(this->val + rhs.val); } };
|
🛠 易错点:
- 忘了
const
(导致无法加常量对象)
- 参数没加引用(浪费性能)
2️⃣ ==
:等于运算符
1 2 3 4 5 6 7
| class MyClass { int val; public: bool operator==(const MyClass& rhs) const { return val == rhs.val; } };
|
🛠 易错点:
- 没加
const
,无法比较常量对象
- 返回
void
类型而非 bool
3️⃣ =
:赋值运算符
1 2 3 4 5 6 7 8 9 10
| class MyClass { int val; public: MyClass& operator=(const MyClass& rhs) { if (this != &rhs) { val = rhs.val; } return *this; } };
|
🛠 易错点:
- 忘了返回
MyClass&
(导致链式赋值出错)
- 忘了处理自赋值
- 参数不是
const MyClass&
4️⃣ <<
:输出运算符(必须友元函数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> using namespace std;
class MyClass { int val; public: MyClass(int v = 0) : val(v) {}
friend ostream& operator<<(ostream& os, const MyClass& obj) { os << obj.val; return os; } };
|
🛠 易错点:
- 写成成员函数(不行,
cout
不属于类)
- 参数顺序错误:应是
(ostream&, const MyClass&)
- 忘了返回
ostream&
,导致链式输出错误
5️⃣ >>
:输入运算符
1 2 3 4 5 6 7 8
| class MyClass { int val; public: friend istream& operator>>(istream& is, MyClass& obj) { is >> obj.val; return is; } };
|
🛠 易错点:
- 参数不能写成
const MyClass&
,因为要修改对象
- 写成成员函数无效(
cin
在左侧)
6️⃣ 前置和后置 ++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class MyClass { int val; public: MyClass& operator++() { ++val; return *this; }
MyClass operator++(int) { MyClass tmp = *this; ++val; return tmp; } };
|
🛠 易错点:
- 写后缀
++
时忘了 int
占位参数
- 返回引用而非值:后缀不能返回引用
7️⃣ []
:下标运算符
1 2 3 4 5 6 7 8 9 10 11
| class MyClass { int arr[10]; public: int& operator[](int index) { return arr[index]; }
const int& operator[](int index) const { return arr[index]; } };
|
🛠 易错点:
- 忘记重载
const
版本
- 返回值不加引用,失去可赋值性
8️⃣ ->
:指针运算符
1 2 3 4 5 6
| class PtrWrapper { MyClass* ptr; public: PtrWrapper(MyClass* p) : ptr(p) {} MyClass* operator->() { return ptr; } };
|
🛠 易错点:
- 忘记返回
MyClass*
- 重载了
->
但对象不是包装类指针
🧾 三、标准模板总结表(重点推荐你记住)
运算符 |
成员函数 / 友元 |
参数 |
返回类型 |
常加 const |
备注 |
+ |
成员 / 友元 |
const T& |
T |
✅ |
建议 const 成员 |
== |
成员 / 友元 |
const T& |
bool |
✅ |
|
= |
成员函数 |
const T& |
T& |
✖️ |
要处理自赋值 |
<< |
友元函数 |
ostream&, const T& |
ostream& |
✅ |
只能写友元 |
>> |
友元函数 |
istream&, T& |
istream& |
✖️ |
修改对象不能加 const |
[] |
成员函数 |
int |
T& / const T& |
视情况而定 |
需重载两个版本 |
++ |
成员函数 |
int for 后缀 |
T / T& |
✖️ |
前置返回引用,后置返回值 |
✅ 四、建议你重点注意的几个事项
- 所有“不会修改成员”的重载函数,加上
const
<<
和 >>
必须写成友元函数
operator=
必须返回引用,并处理自赋值
- 比较、算术、++– 等运算符建议全套重载(便于写通用容器类)
- 所有涉及值返回的操作符,要注意深拷贝风险
类外定义
非常好!你说得对,运算符重载函数类外定义是面试、考试以及实际工程开发中最常见的写法,也是最容易出语法错的地方。
下面我将为你系统整理一份类外定义版的运算符重载笔记,包含:
- ✅ 正确的函数原型(类内声明 + 类外定义)
- ✅ 成员 vs 非成员(友元)写法分清
- ✅ 每种运算符完整示例
- ✅ 常见易错点提示
✅ 一、基础模板说明
🔹 成员函数类外定义格式:
1 2 3 4 5 6 7 8
| class A { public: A operator+(const A& rhs) const; };
A A::operator+(const A& rhs) const { }
|
🔹 友元函数类外定义格式:
1 2 3 4 5 6 7
| class A { friend std::ostream& operator<<(std::ostream&, const A&); };
std::ostream& operator<<(std::ostream& os, const A& obj) { }
|
🧾 二、常见运算符类外定义写法合集
1️⃣ 加法 +
(成员函数)
1 2 3 4 5 6 7 8 9 10
| class MyClass { int val; public: MyClass(int v = 0) : val(v) {} MyClass operator+(const MyClass& rhs) const; };
MyClass MyClass::operator+(const MyClass& rhs) const { return MyClass(this->val + rhs.val); }
|
2️⃣ 比较 ==
(成员函数)
1 2 3 4 5 6 7 8 9
| class MyClass { int val; public: bool operator==(const MyClass& rhs) const; };
bool MyClass::operator==(const MyClass& rhs) const { return val == rhs.val; }
|
3️⃣ 赋值 =
(成员函数)
1 2 3 4 5 6 7 8 9 10 11 12
| class MyClass { int val; public: MyClass& operator=(const MyClass& rhs); };
MyClass& MyClass::operator=(const MyClass& rhs) { if (this != &rhs) { val = rhs.val; } return *this; }
|
4️⃣ 输出 <<
(友元函数)
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <iostream> using namespace std;
class MyClass { int val; public: MyClass(int v = 0) : val(v) {} friend ostream& operator<<(ostream& os, const MyClass& obj); };
ostream& operator<<(ostream& os, const MyClass& obj) { os << obj.val; return os; }
|
🛠️ 注意:不能写成成员函数,因为左侧是 ostream
5️⃣ 输入 >>
(友元函数)
1 2 3 4 5 6 7 8 9 10
| class MyClass { int val; public: friend istream& operator>>(istream& is, MyClass& obj); };
istream& operator>>(istream& is, MyClass& obj) { is >> obj.val; return is; }
|
🛠️ 注意:不能写成成员函数,且 obj
不能加 const
6️⃣ 前置 ++
(成员函数)
1 2 3 4 5 6 7 8 9 10
| class MyClass { int val; public: MyClass& operator++(); };
MyClass& MyClass::operator++() { ++val; return *this; }
|
7️⃣ 后置 ++
(成员函数)
1 2 3 4 5 6 7 8 9 10 11
| class MyClass { int val; public: MyClass operator++(int); };
MyClass MyClass::operator++(int) { MyClass tmp = *this; ++val; return tmp; }
|
🛠️ int
是占位参数,代表后缀
8️⃣ 下标运算符 []
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class MyClass { int data[10]; public: int& operator[](int index); const int& operator[](int index) const; };
int& MyClass::operator[](int index) { return data[index]; }
const int& MyClass::operator[](int index) const { return data[index]; }
|
9️⃣ 箭头运算符 ->
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class MyClass { public: void hello() const { std::cout << "Hi\n"; } };
class Wrapper { MyClass* ptr; public: Wrapper(MyClass* p) : ptr(p) {} MyClass* operator->(); };
MyClass* Wrapper::operator->() { return ptr; }
|
🚨 三、常见语法错误回顾(结合你以往的问题)
错误表现 |
正确写法 |
<< 写成成员函数 |
friend ostream& operator<<(ostream&, const T&) |
operator++(int) 忘记 int |
后缀重载必须加占位参数 |
比较函数忘 const |
bool operator==(const T&) const |
[] 返回值类型不对 |
返回 T& / const T& |
= 返回值错误 |
必须返回 T& ,不能返回 void 或 T |
没有使用初始化列表 |
构造拷贝类时要用初始化列表提高效率 |
✅ 四、结语:如何掌握类外定义写法?
建议你遵循这三步训练:
- 动手写完整一套带类外定义的类:支持
+
==
<<
等
- 背诵“运算符函数三件套”:参数、返回、const(函数签名)
- 做几道语法查错题:比如“以下重载写法有什么问题?”
非常棒,这是最值得精读的部分之一!
🧾 C++完整模板类 + 常用运算符类外重载实现
我们以一个通用的 Vector2D
类为例,封装了常用的全部运算符重载(类外定义版本):
- ✅ 算术:
+ - * /
- ✅ 比较:
== != < > <= >=
- ✅ 赋值:
=
- ✅ 输入输出:
<< >>
- ✅ 自增:
++ --
(前置和后置)
- ✅ 下标:
[]
- ✅ 复合赋值:
+= -= *= /=
- ✅ 自定义拷贝构造、移动构造、拷贝赋值、移动赋值(完整 Rule of Five)
✅ 完整代码:Vector2D.h
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
| #ifndef VECTOR2D_H #define VECTOR2D_H
#include <iostream> #include <stdexcept> using namespace std;
class Vector2D { private: double x, y;
public: Vector2D(double x = 0.0, double y = 0.0);
Vector2D(const Vector2D& other); Vector2D(Vector2D&& other) noexcept; Vector2D& operator=(const Vector2D& other); Vector2D& operator=(Vector2D&& other) noexcept; ~Vector2D();
double& operator[](int index); const double& operator[](int index) const;
Vector2D& operator++(); Vector2D operator++(int);
Vector2D& operator--(); Vector2D operator--(int);
Vector2D& operator+=(const Vector2D& rhs); Vector2D& operator-=(const Vector2D& rhs); Vector2D& operator*=(double scalar); Vector2D& operator/=(double scalar);
friend Vector2D operator+(const Vector2D& a, const Vector2D& b); friend Vector2D operator-(const Vector2D& a, const Vector2D& b); friend Vector2D operator*(const Vector2D& a, double scalar); friend Vector2D operator*(double scalar, const Vector2D& a); friend Vector2D operator/(const Vector2D& a, double scalar);
friend bool operator==(const Vector2D& a, const Vector2D& b); friend bool operator!=(const Vector2D& a, const Vector2D& b); friend bool operator<(const Vector2D& a, const Vector2D& b); friend bool operator>(const Vector2D& a, const Vector2D& b); friend bool operator<=(const Vector2D& a, const Vector2D& b); friend bool operator>=(const Vector2D& a, const Vector2D& b);
friend ostream& operator<<(ostream& os, const Vector2D& v); friend istream& operator>>(istream& is, Vector2D& v); };
#endif
|
✅ 实现文件:Vector2D.cpp
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
| #include "Vector2D.h" #include <cmath>
Vector2D::Vector2D(double x, double y) : x(x), y(y) {} Vector2D::Vector2D(const Vector2D& other) : x(other.x), y(other.y) {} Vector2D::Vector2D(Vector2D&& other) noexcept : x(other.x), y(other.y) {} Vector2D::~Vector2D() = default;
Vector2D& Vector2D::operator=(const Vector2D& other) { if (this != &other) { x = other.x; y = other.y; } return *this; } Vector2D& Vector2D::operator=(Vector2D&& other) noexcept { x = other.x; y = other.y; return *this; }
double& Vector2D::operator[](int index) { if (index == 0) return x; if (index == 1) return y; throw out_of_range("Index out of range"); } const double& Vector2D::operator[](int index) const { if (index == 0) return x; if (index == 1) return y; throw out_of_range("Index out of range"); }
Vector2D& Vector2D::operator++() { ++x; ++y; return *this; }
Vector2D Vector2D::operator++(int) { Vector2D temp = *this; ++(*this); return temp; }
Vector2D& Vector2D::operator--() { --x; --y; return *this; }
Vector2D Vector2D::operator--(int) { Vector2D temp = *this; --(*this); return temp; }
Vector2D& Vector2D::operator+=(const Vector2D& rhs) { x += rhs.x; y += rhs.y; return *this; } Vector2D& Vector2D::operator-=(const Vector2D& rhs) { x -= rhs.x; y -= rhs.y; return *this; } Vector2D& Vector2D::operator*=(double scalar) { x *= scalar; y *= scalar; return *this; } Vector2D& Vector2D::operator/=(double scalar) { x /= scalar; y /= scalar; return *this; }
Vector2D operator+(const Vector2D& a, const Vector2D& b) { return Vector2D(a.x + b.x, a.y + b.y); } Vector2D operator-(const Vector2D& a, const Vector2D& b) { return Vector2D(a.x - b.x, a.y - b.y); } Vector2D operator*(const Vector2D& a, double scalar) { return Vector2D(a.x * scalar, a.y * scalar); } Vector2D operator*(double scalar, const Vector2D& a) { return a * scalar; } Vector2D operator/(const Vector2D& a, double scalar) { return Vector2D(a.x / scalar, a.y / scalar); }
bool operator==(const Vector2D& a, const Vector2D& b) { return a.x == b.x && a.y == b.y; } bool operator!=(const Vector2D& a, const Vector2D& b) { return !(a == b); } static double magnitude(const Vector2D& v) { return sqrt(v.x * v.x + v.y * v.y); } bool operator<(const Vector2D& a, const Vector2D& b) { return magnitude(a) < magnitude(b); } bool operator>(const Vector2D& a, const Vector2D& b) { return magnitude(a) > magnitude(b); } bool operator<=(const Vector2D& a, const Vector2D& b) { return !(a > b); } bool operator>=(const Vector2D& a, const Vector2D& b) { return !(a < b); }
ostream& operator<<(ostream& os, const Vector2D& v) { os << "(" << v.x << ", " << v.y << ")"; return os; } istream& operator>>(istream& is, Vector2D& v) { is >> v.x >> v.y; return is; }
|
✅ 如何使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include "Vector2D.h" int main() { Vector2D a(3, 4), b(1, 2); Vector2D c = a + b; cout << "a + b = " << c << endl;
c *= 2; cout << "c *= 2 = " << c << endl;
cout << "Magnitude comparison (a > b): " << (a > b) << endl;
++a; cout << "++a = " << a << endl;
cout << "a[0] = " << a[0] << ", a[1] = " << a[1] << endl;
return 0; }
|
🧠 总结
这个 Vector2D
类模板实现了C++常见重载几乎全套范式,你可以用它来:
- 📚 复习类外重载写法
- ✍️ 练习函数签名记忆
- 🧪 自定义类进行工程建模