static
static member 被Class的所有实例共享
static: persist maintainance(持久存储)
static的初始化构造命令只会执行和发生一次
- static member 的类属性变量——必须在类外初始化定义,你在构造一个类的对象的时候,是不会自己自动初始化的; 可以通过类来访问:
A::date
static function
函数:只能访问/操作 static类型的成员变量;可以通过类来调用A::function();
Operation Overloaded
operators 运算符本质上也是一种member function
这里面的 z=x+3 3其实是作隐式转换为z和x的类型
单目运算的参数表是空的(只有对象自己参与)
通过Global Function + friend
来实现重载
- 注意一定要用
const Type&
常量引用,不然不能接收临时对象——“const reference”
- 能加const一定加上,包括函数不改变值的时候
特殊一点的[]
运算符——“下标访问”
- 返回值是你要访问的元素的引用 > 这种的一般都是要修改容器内元素的,所以返回引用,属于非const型函数,所以并不能用于访问const型的容器内的const元素
- 所以往往还有另一种
[]
的重载!
在 C++ 中,
[]
操作符的重载可以通过 const
和非 const
版本实现:- 非
const
版本: - 允许修改对象的元素。
- 返回类型通常是可变的引用(
T&
),支持vec[i] = value
这样的赋值操作。
const
版本:- 适用于
const
对象,只允许读取,不允许修改。 - 返回类型是常量引用(
const T&
),保证只能读取元素值,不能更改。
通过同时提供
const
和非 const
版本,确保 []
可以既用于常量对象的访问(只读),也用于非常量对象的访问和修改,从而保证了灵活性和安全性。重载时区分i++和++i
在设计重载函数的时候用一个参数在后面进行占位来区分
- 有占位的是
i++
- 没占位的是
++i
逻辑比较运算——注意重用
- 理论上只用定义一个
<
后面都可以逻辑重用
Assignment operator=
- 编译器一般可以自动生成(每个member都递归调用
=
赋值operator)
特别特殊————functor ()
小括号操作符
Lambda表达式
Lambda 表达式 是 可以直接当成一个函数function对象
Lambda 表达式的基本语法
- 捕获列表
[]
:捕获当前作用域的变量,按值或引用捕获。
- 参数列表
()
:函数的参数。
- 返回类型
>
:指定函数的返回类型(可省略)。
- 函数体
{}
:定义具体逻辑。
示例:基础用法
捕获列表
- 按值捕获
[x]
:创建当前变量的副本。
- 按引用捕获
[&x]
:引用变量,修改会影响原变量。
[=]
:按值捕获所有外部变量。
[&]
:按引用捕获所有外部变量。
示例:
应用场景
- STL 算法:
- 回调函数:Lambda 可以用作临时函数对象。
Lambda 表达式的底层本质就是——functor ()
二者关系
- Lambda 表达式:由编译器生成一个匿名类,并实现了
operator()
,因此可以被像函数一样调用。
- 仿函数:是一个显式定义的类,通过重载
operator()
使得对象具有函数调用行为。
简洁示例对比:
+ Lambda 表达式:
- 等价的仿函数:
Type Conversion Operater 类型转换运算符
- 第一种:通过定义有参数的Ctor,可以实现类似类型转换的效果
- 第二种:
operator double() const {}
例如:
Fraction::operator double() const{ return static_cast<double>(numerator) / denominator;}
这种
operator double() const
是不需要写返回值类型的- 关键词:
explicit
声明为explicit
的ctor或者类型转换符,不能再作自动的隐式类型转换,每一次转换必须都是显式的转换
- 如果你两种都写了,会冲突报错Ambiguous编译器不知道用哪种
对于<<
>>
流操作符的重载
- 都是通过
全局函数重载
+声明为友元函数
来实现 - 成员函数写不成
一个operator<<
重载的教训——要用 const tpye&型引用
### Grannypuppy:
#file:fraction.cpp #file:fraction.h 为什么 cout<<f2-f3 会默认转换为double型输出。我的<<重载里面不是让他显式地转换为string输出么
### GitHub Copilot:
问题出在
operator<<
的重载函数中。你将 Fraction对象转换为 std::string,但在 operator<<
函数中传递的是Fraction对象的引用。为了确保Fraction对象在输出时总是转换为字符串,你需要将 operator<<
函数的参数类型改为 const Fraction&
。请修改 fraction.cpp 中的 operator<<
函数如下:同时,确保在 fraction.h 中的声明也一致:
friend std::ostream& operator<<(std::ostream& os, const Fraction& fraction);
这样可以确保 Fraction 对象在使用 <<
操作符时总是转换为字符串输出。
### Grannypuppy:
我修改后确实成功了,这是为什么
### GitHub Copilot:
这是因为在 C++ 中,运算符重载函数的参数类型会影响到函数的匹配和调用。具体来说:
1. 常量引用(const Fraction&
):- 使用常量引用可以确保传递给函数的对象不会被修改。
- 这也允许函数接受临时对象(如表达式的结果),因为临时对象只能绑定到常量引用。
2. 非常量引用(Fraction&):
- 非常量引用要求传递给函数的对象必须是一个左值(即一个可以被修改的对象)。
- 这意味着临时对象(如表达式的结果)不能绑定到非常量引用。
在你的代码中,
f2 - f3
产生了一个临时的 Fraction 对象。这个临时对象不能绑定到非常量引用(Fraction&
),但可以绑定到常量引用(const Fraction&
)。因此,将 operator<<
的参数类型改为 const Fraction&
允许它接受临时对象,从而正确调用重载的 <<
运算符并输出字符串表示。
# 总结:- 使用 const Fraction& 作为参数类型可以确保函数接受临时对象,并且不会修改传递的对象。
- 这使得 `operator<<` 可以正确处理表达式的结果(如 f2 - f3),并调用 `std::string` 转换运算符来输出字符串表示。
承接上文的临时变量
左值(Lvalue)和右值(Rvalue)是C++中的两个基本概念。
- 左值(Lvalue):表示一个在内存中有确定存储地址的对象,可以取地址。左值可以出现在赋值操作符的左边或右边。
- 右值(Rvalue):表示一个不占据内存中确定存储地址的值,通常是临时对象或字面值。右值只能出现在赋值操作符的右边。
在C++中,非const的左值引用(
Fraction&
)不能绑定到右值(临时对象),因为右值是临时的,生命周期很短,绑定到非const引用可能会导致悬挂引用(dangling reference)的问题。而const左值引用(const Fraction&
)可以绑定到右值,因为const引用不会修改对象,编译器会延长临时对象的生命周期以确保引用的安全性。示例:
总结
- 非const左值引用(
Fraction&
)只能绑定到左值。
- const左值引用(
const Fraction&
)可以绑定到左值和右值。