唔.. 这大概是 operator 的笔记叭.. 不知道自己为啥要写前半段没用的东西 qwq
# 好像没用的部分
# C++ 运算符
# 运算符与函数
运算符举例:
- string 类:
+
连接字符串。"Hello" + " Chino!"
- array/vector 类:
[]
访问元素。v[0] = 233;
- path 类:
/
连接路径元素。p = p / "C" / "Users" / "Chino";
运算符可以看作函数,不同处:
- 语法区别,前中后缀什么什么的..
- 不能定义新运算符。
- 运算符作用域内置类型的行为不能修改,例如
2 * 3
结果不能是 1。
# 平面向量类
开始重复造轮
- 数据成员:x, y 或 array<double, 2> v
- 运算方法:+, -, 数乘,点乘,长度,方向,==, !=, <,>, <=, >= ...
- TDD 测试驱动开发:
- 编写运行测试代码。
- 改动,尽快让测试程序可运行。
- 重构代码,优化设计。
- 测试代码:
// 创建对象 | |
Vec2D v1{3, 5}, v2{4, 6}; | |
// 转为字符串输出 | |
cout << "v1 = " << v1.toString() << endl; | |
cout << "v2 = " << v2.toString() << endl; | |
// 向量加法 | |
Vec2D v3 = v1.add(v2); | |
Vec2D v4 = v3.add(10.0); | |
cout << "v3 = " << v3.toString() << endl; | |
cout << "v4 = " << v4.toString() << endl; | |
// 向量减法,点积,数乘 | |
Vec2D v5 = v2.subtract(v1); | |
double v6 = v2.dot(v1); | |
Vec2D v7 = v3.multiply(2.1); | |
cout << "v2 - v1 = " << v5.toString() << endl; | |
cout << "v2 . v1 = " << v6 << endl; | |
cout << "v3 * 2.1 = " << v7.toString() << endl; | |
// 取反 | |
Vec2D v8 = v2.negative(); | |
cout << "-v2 = " << v8.toString() << endl; | |
// 自增,自减 | |
cout << "++v8 = " << v8.increase().toString() << endl; | |
cout << "--v2 = " << v2.increase().toString() << endl; | |
// 读取,修改 | |
cout << "v1.x_ = " << v1.at(0) << endl; | |
cout << "v1.y_ = " << v1.at(1) << endl; | |
// 长度与角度 | |
cout << "v1.magnitude = " << v1.magnitude() << endl; | |
cout << "v1.direction = " << v1.direction() << endl; | |
// 比较两向量 | |
cout << "v1 ? v2 : " << v1.compareTo(v2) << endl; |
- 编写平面向量类,实现测试功能。
class Vec2D | |
{ | |
private: | |
double x_, y_; | |
public: | |
Vec2D(double x = 0, double y = 0) | |
{ | |
x_ = x; | |
y_ = y; | |
} | |
~Vec2D() = default; | |
string toString() | |
{ | |
return string("(" + to_string(x_) + ", " + to_string(y_) + ")"); | |
} | |
Vec2D add(Vec2D secondVec2D) | |
{ | |
return Vec2D(this -> x_ + secondVec2D.x_, this -> y_ + secondVec2D.y_); | |
} | |
Vec2D add(double num) | |
{ | |
return Vec2D(this -> x_ + num, this -> y_ + num); | |
} | |
Vec2D subtract(Vec2D secondVec2D) | |
{ | |
return Vec2D(this -> x_ - secondVec2D.x_, this -> y_ - secondVec2D.y_); | |
} | |
Vec2D subtract(double num) | |
{ | |
return Vec2D(this -> x_ - num, this -> y_ - num); | |
} | |
double dot(Vec2D secondVec2D) | |
{ | |
return x_ * secondVec2D.x_ + y_ * secondVec2D.y_; | |
} | |
Vec2D multiply(double multiplier) | |
{ | |
return Vec2D(x_ * multiplier, y_ * multiplier); | |
} | |
Vec2D negative() | |
{ | |
return Vec2D(-x_, -y_); | |
} | |
Vec2D& increase() | |
{ | |
x_++; y_++; | |
return *this; | |
} | |
Vec2D& decrease() | |
{ | |
x_--; y_--; | |
return *this; | |
} | |
double& at(const int index) | |
{ | |
if (0 == index) | |
return x_; | |
if (1 == index) | |
return y_; | |
throw out_of_range("at() only accept 0 or 1 as parameter."); // 抛出异常 | |
} | |
double magnitude() | |
{ | |
return sqrt(x_ * x_ + y_ * y_); | |
} | |
double direction() | |
{ | |
return atan(y_ / x_); | |
} | |
int compareTo(Vec2D secondVec2D) | |
{ | |
double m1 = this -> magnitude(), m2 = secondVec2D.magnitude(); | |
if (fabs(m1 - m2) < 1e-10) | |
return 0; | |
return m1 > m2 ? 1 : -1; | |
} | |
}; |
# 运算符重载
可重载:
类型转换运算符:double, int, char, ……
new/delete, new []/delete[]
""_suffix 用户自定义字面量运算符 (自 C++11 起)
一般运算符。
不可重载:
Operator | Name |
---|---|
. | 类属关系运算符 |
.* | 成员指针运算符 |
:: | 作用域运算符 |
? : | 条件运算符 |
# | 编译预处理符号 |
# 运算符函数
v1 < v2
等价于 v1.operator<(v2)
,定义(接上段):
bool Vec2D::operator<(Vec2D & secondVec2D) | |
{ | |
return this -> compareTo(secondVec2D) < 0; | |
} |
# 左值、右值、将亡值
左值:指定一个函数或对象,是可以取地址的表达式。
- 解引用表达式
*p
- 字符串字面量
"qwq"
- 前自增 / 自减
++i
- 赋值或复合运算符表达式
x = y
或m *= n
纯右值:不与对象相关联(字面量)或求值结果是字面量的匿名临时对象。
- 普通字面量:
'a'
,233
- 返回非引用的函数调用
int f() { return 1; }
- 后自增 / 自减
i++
- 算数 / 逻辑 / 关系表达式 / 取地址
a + b
a < b
a != b
&a
将亡值:将纯右值转化为右值引用的表达式。(傻...owo
# 大概有用的部分
语法: 返回类型 operator 运算符(参数) { 函数体 }
# 算术 + - * /
- 返回值一般是右值。
- 运算符函数的参数类型可以是引用,最好用 const 修饰。
实例:
class Vec2D | |
{ | |
private: | |
double x_, y_; | |
public: | |
Vec2D(double x = 0, double y = 0) | |
{ | |
x_ = x; | |
y_ = y; | |
} | |
~Vec2D() = default; | |
string toString() | |
{ | |
return string("(" + to_string(x_) + ", " + to_string(y_) + ")"); | |
} | |
Vec2D operator +(const Vec2D &secondVec2D) | |
{ | |
return vec2D(x_ + secondVec2D.x_, y_ + secondVec2D.y_); | |
} | |
// 不能 double::operator+(Vec2D),只能友元 | |
friend Vec2D operator *(const double&, const Vec2D&); | |
}; | |
Vec2D operator *(const double& num, const Vec2D& vec) | |
{ | |
return Vec2D(num * vec.x_, num * vec.y_); | |
} | |
int main() | |
{ | |
Vec2D v1{3, 5}, v2{4, 6}; | |
// 向量加法,乘法 | |
Vec2D v3 = v1 + v2; | |
Vec2D v4 = 2.0 * v1; | |
cout << "v3 = " << v3.toString() << endl; | |
cout << "v4 = " << v4.toString() << endl; | |
return 0; | |
} |
# 赋值 += -= *= /= =
# += -= *= /=
采用
this
改变自身值。operator 返回值类型一般写成引用。
实例:(接上段)
public: | |
// 这里不加 & amp; 的话,像 (v1 += v2) += v2; 之类的操作会炸掉 qwq | |
Vec2D& operator += (const Vec2D &secondVec2D) | |
{ | |
*this = this -> add(secondVec2D); | |
return *this; | |
} | |
int main() | |
{ | |
Vec2D v1{3, 5}, v2{4, 6}; | |
v1 += v2; | |
cout << "v1 = " << v1.toString() << endl; // 7, 11 | |
return 0; | |
} |
# =
- 默认进行一对一浅拷贝。
- 需要深拷贝时,重载赋值
=
运算符。
# 比较 > == < >= <= !=
- 重载 < 常用于 STL 里的各种操作。
- 使用友元。
# 下标访问 [ ]
并没有什么意料之外的:
class Vec2D | |
{ | |
private: | |
double x_, y_; | |
public: | |
double& operator [] (const int &index) | |
{ | |
if (index == 0) | |
return x_; | |
if (index == 1) | |
return y_; | |
else | |
throw out_of_range("at() only accept 0 or 1 as parameter."); | |
} | |
}; | |
int main() | |
{ | |
Vec2D v1{3, 5}; | |
v1[0] += 1; | |
cout << "v1.x_ = " << v1[0] << endl; // 4 | |
return 0; | |
} |
- 假设 @为某单目运算符,编译器遇到
@obj
时, 是否传参取决于operator @ ()
是成员函数operator @ ()
还是友元函数operator @ (Type &) //可能需要引用
。
# -
Vec2D Vec2D::operator - () | |
{ | |
return Vec2D(-x_, -y_); // 临时匿名对象 | |
} |
# 自增 / 自减 ++/--
前缀返回值为左值
后缀返回值为右值
运算符 | 类内定义 | 类外定义 |
---|---|---|
++a | T& T::operator ++ (); | T& operator ++ (T& a); |
a++ | T T::operator ++ (int dummy); | T T::operator ++ (T& a, int dummy); |
dummy
占位符,用于区分前置与后置,调用时并不会传递参数。
实例:
class Vec2D | |
{ | |
private: | |
double x_, y_; | |
public: | |
Vec2D(double x = 0, double y = 0) | |
{ | |
x_ = x; | |
y_ = y; | |
} | |
~Vec2D() = default; | |
string toString() | |
{ | |
return string("(" + to_string(x_) + ", " + to_string(y_) + ")"); | |
} | |
Vec2D& operator ++() | |
{ | |
++x_; ++y_; | |
return *this; | |
} | |
Vec2D operator ++(int dummy) | |
{ | |
Vec2D tmp = Vec2D(x_, y_); | |
x_++; y_++; | |
return tmp; | |
} | |
}; | |
int main() | |
{ | |
Vec2D v1{3, 5}; | |
cout << "v1++ = " << v1++.toString() << endl; // 3, 5 | |
cout << "++v1 = " << (++v1).toString() << endl; // 5, 7 | |
(++v1)++; | |
cout << "v1 = " << v1.toString() << endl; // 7, 9 | |
return 0; | |
} |
# 流插入 / 提取运算符 >>/<<
- 是二元运算符,第一个操作数是 cin 或 cout
- 因为要把自定义对象作为第二个操作数~~(不会吧不会吧不会真的有人想把代码写得像💩一样难看吧)~~,所以只采用友元。
实例:
#include <iostream> | |
using namespace std; | |
class Vec2D | |
{ | |
private: | |
double x_, y_; | |
public: | |
Vec2D(double x = 0, double y = 0) | |
{ | |
x_ = x; | |
y_ = y; | |
} | |
~Vec2D() = default; | |
friend ostream& operator << (ostream&, const Vec2D&); | |
friend istream& operator >> (istream&, Vec2D&); | |
}; | |
ostream& operator << (ostream &os, const Vec2D& v) // 返回 ostream& 以连续使用流插入运算符 | |
{ | |
return os << "(" << v.x_ << ", " << v.y_ << ")"; | |
} | |
istream& operator >> (istream &is, Vec2D& v) // 别带 const | |
{ | |
return is >> v.x_ >> v.y_; | |
} | |
int main() | |
{ | |
Vec2D v1, v2; | |
cin >> v1 >> v2; // 1 2 3 4 | |
cout << v1 << " qwq " << v2 << endl; // (1, 2) qwq (3, 4) | |
return 0; | |
} |
# 对象转换 static_cast<Type>()
语法
operator Type() { return value; }
没有返回值类型。(ctor, dtor 也没有)
包括隐式类型转换与强制类型转换。
# ex
- 不允许操作基础数据类型,参数至少应有一个自定义类的对象。
- 以友元函数重载,可以更灵活地指定次序。(部分运算符如 [ ] 不能以友元重载)