使用类
-
运算符重载
- 重载后的运算符必须至少有一个操作数是用户定义的类型
- 使用运算符时不能违反原来运算符的句法规则, 同样不能修改运算符的优先级
- 不能创建新的运算符, 例如定义operator**()函数来表示求幂
- 不能重载以下运算符
- sizeof : sizeof运算符
- . : 成员运算符
- .* : 成员指针运算符
- :: : 作用域解析运算符
- ?: : 条件运算符
- typeid : 一个RTTI运算符
- const_cast, dynamic_cast, reinterpret_cast, static_cast : 强制类型转换运算符
- 以下运算符只能通过成员函数重载
- = : 赋值运算符
- () : 函数调用运算符
- [] : 下标运算符
-> : 通过指针访问类成员的运算符
-
以下运算符可通过成员函数或非成员函数重载
-
友元
- 友元有三种:
- 友元函数
- 友元类
- 友元成员函数
通过让函数成为类的友元,可以赋予该函数与类成员函数相同的访问权限
- 为何需要友元
在为类重载二元运算符时, 常常需要友元
双目运算符交换两边操作数结果应该是一样的, 为了满足两种用法, 非成员函数重载运算符为了能够使用类的私有成员将函数声明为类的友元函数
创建友元函数的方法如下代码:
class Point {
int x;
int y;
public:
Point(int xv = 0, int yv = 0): x(xv), y(yv) {}
Point operator*(int fac) const {
Point res;
res.x = x * fac;
res.y = y * fac;
return res;
}
// 1. 将函数原型放在类声明中, 并加上关键字friend
friend Point operator*(int fac, const Point& p);
void show() {
cout << "(" << x << ", " << y << ")\n";
}
};
// 2. 编写定义函数, 他不是成员函数不要加Point::限定
// 另外注意不能在定义中使用friend关键字, 除非为内联函数的情况(原型也是定义)
Point operator*(int fac, const Point& p) {
Point res;
res.x = p.x * fac;
res.y = p.y * fac;
return res;
}
- 常用的友元: 重载<<运算符
用cout << name 来输出用户定义类型
一种实现代码如下:
// 在类中声明原型
friend void operator<<(ostream & os, const Point& p);
// 定义
void operator<<(ostream & os, const Point& p) {
os << "(" << p.x << ", " << p.y << ")\n";
}
另一种更好的实现, 适应cout << x << y这种用法
代码如下:
ostream & operator<<(ostream & os, const Point& p) {
os << "(" << p.x << ", " << p.y << ")\n";
return os;
}
-
类的自动类型转换和强制类型转换
- C++的类型转换
- 将一个标准类型变量的值赋给另一种标准类型的变量时, 如果这两种类型兼容, 则C++自动讲这个值转换为接收变量的类型
long a = 22; // type int -> type long double b = 33; // int c = 3.14; //
- C++不自动转换不兼容的类型, 例如:
int* p = 10;
- 但是可以使用强制类型转换
int* p = (int*)10;
- 将构造函数用作自动类型转换
class ConvInt {
public:
ConvInt(int x) { m_x = x; }
private:
int m_x;
}
ConvInt ci = 10; // 合法
- explicit关键字用于关闭这种特性, 但仍可以显示转换
class ConvInt {
public:
explicit ConvInt(int x) { m_x = x; }
private:
int m_x;
}
ConvInt ci = 10; // 不合法
ConvInt cj = ConvInt(10); // 合法
ConvInt ck = (ConvInt)10; // 合法
- 注意: 只接收一个参数的构造函数定义了从参数类型到类类型的转换
- 何时使用ConvInt(int)函数?
如果在声明中使用了关键字explicit, 则ConvInt(int)将只用于显示的强制转换, 否则还可以用于以下隐式转换:
- 将ConvInt对象初始化为int值时
- 将int值赋给ConvInt对象时
- 将int值传递给接受ConvInt参数的函数时
- 返回值被声明为ConvInt的函数试图返回int值时
- 在上述任一种情况下,使用可转换为int类型的内置类型时
对以上最后一条而言转换不能出现二义性
- 转换函数
转换函数用于将类类型转换为某种类型
例如有以下语句ConvInt ci(10); double dt1 = double(ci); double dt2 = (double)ci; double dt3 = ci;
转换函数应满足以下几点:
- 转换函数必须是类方法
- 转换函数不能指定返回类型
- 转换函数不能有参数
例如, 转换为double类型的原型声明如下:
operator double();
- 程序中不能出现二义性转换, 例如:
class ConvInt {
public:
ConvInt(int x) { m_x = x; }
operator int() {
return m_x;
}
operator double() {
return double(m_x);
}
private:
int m_x;
}
ConvInt ci(1);
double dt = ci; // 合法, 根据上下文知道使用operator double()转换
cout << ci; // 不合法, 编译器会认为有二义性而拒绝
cout << int(ci); // 合法
- 隐式自动转换请谨慎使用, 建议永远做显式转换