📘 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; // 打印全局 x,即 10
}

✅ 三、你可能遇到的真实错误例子(回顾性质)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class A {
void show(); // 声明
};
void show() { ... } // ❌ 缺少 A::

class A {
static int val;
};
int val = 10; // ❌ 应该写成 int A::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 {
// 继承Base的public为public,protected为protected
};
  • 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; // ✅ OK,B类中可访问
}
};

class C : public B {
public:
void tryAccess() {
cout << val; // ❌ 错:val在C中不可见(因A被private继承)
}
};

🛠 易错点:派生类继承链中对访问权限传播判断错误


📍【易错点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 { };

// D 同时继承 B 和 C ⇒ 两份 A
class D : public B, public C {
public:
void access() {
val = 5; // ❌ 编译报错:val 不明确
}
};

🛠 易错点:成员二义性(两个 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; // ✅ OK,只有一份 A
}
};

🛠 易错点

  • 忘记加 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) {} // ❌ 错误!B不能初始化A(虚继承中不生效)
};
class D : public B {
public:
D() : A(10), B() {} // ✅ OK,最底层D初始化A
};

📌 菱形继承结构图(文字版)

1
2
3
4
5
  A
/ \
B C
\ /
D

不加 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; // ❌ 错:二义性 A::id or B::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"; } // ❌ 非虚函数,无法实现多态
};

🛠 建议:凡是你想子类重写的函数,都要 virtualoverride


📍【易错点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(); // 调用的是 Base1 的虚表

✅ 总结:继承中最常出错的地方

类别 易错点
:: 成员函数类外定义、静态变量定义写错作用域
普通继承 忘记用初始化列表构造父类、权限传播判断错误
多继承 成员函数/变量重名未显式指明、虚函数表冲突
菱形继承 忘记 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) {}

// ✅ 加法 - 成员函数(必须是 this + rhs 的形式)
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;
}

// 后置 ++(int 是标记)
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& ✖️ 前置返回引用,后置返回值

✅ 四、建议你重点注意的几个事项

  1. 所有“不会修改成员”的重载函数,加上 const
  2. <<>> 必须写成友元函数
  3. operator= 必须返回引用,并处理自赋值
  4. 比较、算术、++– 等运算符建议全套重载(便于写通用容器类)
  5. 所有涉及值返回的操作符,要注意深拷贝风险

类外定义

非常好!你说得对,运算符重载函数类外定义是面试、考试以及实际工程开发中最常见的写法,也是最容易出语法错的地方。

下面我将为你系统整理一份类外定义版的运算符重载笔记,包含:

  • ✅ 正确的函数原型(类内声明 + 类外定义)
  • ✅ 成员 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&,不能返回 voidT
没有使用初始化列表 构造拷贝类时要用初始化列表提高效率

✅ 四、结语:如何掌握类外定义写法?

建议你遵循这三步训练:

  1. 动手写完整一套带类外定义的类:支持 + == <<
  2. 背诵“运算符函数三件套”:参数、返回、const(函数签名)
  3. 做几道语法查错题:比如“以下重载写法有什么问题?”

非常棒,这是最值得精读的部分之一!


🧾 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);

// Rule of Five
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_H

✅ 实现文件: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++常见重载几乎全套范式,你可以用它来:

  • 📚 复习类外重载写法
  • ✍️ 练习函数签名记忆
  • 🧪 自定义类进行工程建模