+ | - | * | / | % |
^ | & | | | ~ | ! |
= | > | < | += | -= |
*= | /= | %= | ^= | &= |
|= | >> | << | >>= | <<= |
== | != | >= | <= | && |
|| | ++ | -- | ->* | , |
-> | [] | () | operator new | operator new[] |
operator delete | operator delete [] |
:: | . | .* | ? : |
sizeof | typeid | new | delete |
static_cast | dynamic_cast | const_cast | reinterpret_cast |
1.一元操作符可以是不带参数的成员函数[1]或带一个参数的非成员函数[1]。
2.二元操作符可以是带一个参数的成员函数[1]或带两个参数的非成员函数[1]。
3.operator=、operator[]、operator()、operator->只能定义为成员函数[1]。
4.operator->的返回值必须是一个指针或能使用->的对象。
5.重载 operator++ 和 operator-- 时带一个 int 参数表示后缀,不带参数表示前缀。
6.除 operator new 和 operator delete 外,重载的操作符参数中至少要有一个非内建数据类型。
7.x@y 搜索范围为:x 成员函数--> 全局函数/X所在名字空间中的函数/Y所在名字空间中的函数/X的友元函数/Y的友元函数。
8.重载的的操作符应尽量模拟操作符对内建类型的行为。
1.只将会改变第一个参数的值的操作符(如: +=)定义为成员函数,而将返回一个新对象的操作符(如: +)定义为非成员函数(并使用 += 来实现)。
2.只有非成员函数才能在左参数上实施性别转换,如果需要进行转换则应将操作符定义为非成员函数。
3.对一元操作符, 为避免隐式转换最好将其重载为成员函数。
4.对二元操作符, 为能在左操作数上能进行和右操作数一样的隐式转换, 最好将其重载为非成员函数。
5.为了遵照使用习惯,operator>>、operator<< 应定义为非成员函数。
6.重载 operator[] 之类的操作符, 应尽量提供 const 版本和非 const 版本。
7.关于将操作符定义为成员或非成员可参考以下建议:
| ||||||||||||
|
8.如果默认操作符已经可以施用于你的型别上, 则应尽量避免重载此操作符. 如 operator, 、operator&(取地址) 等等.
1.为什么要重载 operator new ?
[改变行为] 默认的分配器失败时会抛出异常, 或许你想改变这种行为.
2. operator new 的行为
new 操作符(new 表达式, new operator, new expression): 通常我们调用 X * pX = new X 时使用的就是这个操作符, 它由语言内建, 不能重载, 不能改变其行为. 它包括分配内存的 operator new 和调用构造函数的 placement new.
operator new :opeator new 是一个函数, void * operator new(size_t size) . 它分配指定大小的内存, 可以被重载, 可以添加额外的参数, 但第一个参数必须为 size_t. operator new 除了被 new operator 调用外也可以直接被调用: void * rawMem = operator new(sizeof(X)).
placement new : placement new 在一块指定的内存上使用构造函数, 包含头文件 <new> 之后也可以直接使用 placement new: X * pX = new (rawMem) X. [2]
与 new operator 类似, 对于 delete operator, 也存在 operator delete: void operator delete(void *), 析构方法 pX->~X().
[operator new 的错误处理]
默认的 operator new 在内存分配失败时将会抛出 std::bad_alloc 异常; nothrow new [3]
(X * pX = new (nothrow) X) 在内存分配失败时将会返回 0 . 这种行为可以通过设置 new-handler 来改变. new-handler 是一个回调函数指针, typedef void(*new_handler)(void). 通过 set_new_handler(new_handler) 函数设置回调句柄后, 如果分配内存失败, operator new 将会不断的调用 new-handler 函数, 直到找到足够的内存为止. 为了避免死循环, new-handler 函数必须具备以下行为之一:
(1).找到可用的内存.
(2).安装其它的 new-handler 函数.
(3).卸除 new-handler, 即 set_new_hanlder(0), 这样下此循环将恢复默认行为抛出异常或返回 0.
(4).抛出异常.
(5).保存错误日志, 退出程序.
3.准备重载 operator new
{
if(size == 0)
size = 1;
while(1)
{
... // allocate memery
if(allocate sucessfull)
return ... // return the pointer.
new_handler curhandler = set_new_handler(0);
set_new_handler(curhandler); // get current new handler
if(curhandler == 0)
(*curhandler)();
else
throw std::bad_alloc();
}
}
[★ 用不同的参数重载 operator new]
通过使用不同的参数类型, 可以重载 operator new, 例如 :
{
...
}
X * pX = new (1, 2, 3) X;
{
...
}
X * pX = new (10) X;
Y * pY = new (10, 10) Y;
Z * pZ = new (10, 10, 10) Z;
...
void * operator new(size_t size, ...)
...
[★ 重载 class 专属的 operator new]
为某个 class 重载 operator new 时 必须定义为类的静态函数[4], 因为 operator new 会在类的对象被构建出来之前调用. 即是说调用 operator new 的时候还不存在 this 指针, 因此重载的 operator new 必须为静态的. 当然在类中重载 operator new 也可以添加额外的参数, 并可以使用默认值.另外, 和普通函数一样, operator new 也是可以继承的.
...
static void * operator new(size_t size); // ... (1)
static void * operator new(size_t size, int); // ... (2)
};
class Y : public X{
...
};
class Z : public X{
...
static void * operator new(size_t size); // ... (3)
};
X * pX1 = new X; // call (1)
X * pX2 = ::new X; // call default operator new
X * pX3 = new (0) X; // call (2)
Y * pY1 = new Y; // call (1)
Z * pZ1 = new Z; // call (3)
Z * pZ2 = ::new Z; // call default operator new
Z * pZ3 = X::new Z; // error, no way to call (1)
Z * pZ4 = new (0) Z; // error, no way to call (2)
5.重载 operator delete
如果在类中使用 operator delete, 也必须将其声明为静态函数. 因为调用 operator delete 时对象已经被析构掉了. operator delete 的重载可以有两种形式:
(1) void operator delete(void * mem)
(2) void operator delete(void * mem, size_t size)
并且这两种形式的 operator delete 可以同时存在, 当调用 delete px 时, 如果 (1) 式存在的话将调用 (1) 式. 只有在 (1) 式不存在时才会调用 (2) 式. 对第 (2) 种形式的 operator delete, 如果用基类指针删除派生类对象, 而基类的析构函数没有虚拟的时候, size 的值可能是错误的.
- const Carrot operator+(const Carrot& lhs, const Carrot& rhs)
Carrot result = lhs;
return result += rhs;
}
【注】1. 如果可能, 应考虑使用 operator+= 来实现 operator+ .
2. operator+ 不能返回引用, 应返回值类型.
3. 为了阻止形如 (a + b) = c 的调用, 应将返回值声明为 const .
· const Carrot operator-(const Carrot& lhs, const Carrot& rhs)
- const Carrot operator*(const Carrot& lhs, const Carrot& rhs)
...
}
【注】1. operator* 还可以 重载为提领操作符.
· const Carrot operator/(const Carrot& lhs, const Carrot& rhs)
· const Carrot operator%(const Carrot& lhs, const Carrot& rhs)
· const Carrot operator^(const Carrot& lhs, const Carrot& rhs)
- const Carrot operator&(const Carrot& lhs, const Carrot& rhs)
...
}
【注】1. operator& 还可以 重载为取地址操作符.
· const Carrot operator|(const Carrot& lhs, const Carrot& rhs)
- const Carrot Carrot::operator-() const
Carrot result = (*this);
... // chang the value to negative
return result;
}
【注】1. 一元操作符, 取负.
· const Carrot Carrot::operator~() const
· bool Carrot::operator!() const
· bool operator>(const Carrot& lhs, const Carrot& rhs)
· bool operator<(const Carrot& lhs, const Carrot& rhs)
- Carrot& Carrot::operator=(const Carrot& rhs)
if(this == &rhs)
return *this; // may be (*this) == rhs if needs.
Barley::operator=(rhs); // if Carrot derived from Barley
... // assignments every memeber of Carrot.
return *this;
}
【注】1. 为了实现形如 x=y=z=0 的串联赋值操作, operator= 必须传回 *this 的非常量引用.
2. 在赋值时应注意检查是否为自赋值 ( a = a ).
- Carrot& Carrot::operator+=(const Carrot& rhs)
...
return *this;
}
【注】1. C++ 允许 (x += 1) = 0 形式的赋值操作, operator+= 必须传回 *this 的非常量引用.
· Carrot& Carrot::operator-=(const Carrot& rhs)
· Carrot& Carrot::operator*=(const Carrot& rhs)
· Carrot& Carrot::operator/=(const Carrot& rhs)
· Carrot& Carrot::operator%=(const Carrot& rhs)
· Carrot& Carrot::operator^=(const Carrot& rhs)
· Carrot& Carrot::operator&=(const Carrot& rhs)
· Carrot& Carrot::operator|=(const Carrot& rhs)
- istream& operator>>(istream& _IStr, Carrot& rhs)
...
return _IStr;
}
【注】1. 为了遵照使用习惯(cin>>x 而不是 x>>cin), 对流操作的 operator>> 应为非成员函数.
- ostream& operator<<(ostream& _OStr, const Carrot& rhs)
...
return _OStr;
}
- const Carrot operator>>(const Carrot& lhs, int rhs)
Carrot result = lhs;
...
return result;
}
【注】1. 移位操作的重载方式.
· const Carrot operator<<(const Carrot& lhs, int rhs)
- Carrot& Carrot::operator>>=(int rhs)
...
return *this;
}
【注】1. 移位操作.
· Carrot& Carrot::operator<<=(int rhs)
- bool operator==(const Carrot& lhs, const Carrot& rhs)
...
}
· bool operator!=(const Carrot& lhs, const Carrot& rhs)
· bool operator>=(const Carrot& lhs, const Carrot& rhs)
· bool operator<=(const Carrot& lhs, const Carrot& rhs)
- bool operator&&(const Carrot& lhs, const Carrot& rhs) X
...
}
【注】1. 基于以下原因, 你应该避免重载 operator&& 和 operator|| :
(1). && 和 || 是一个逻辑操作符, 只对 bool 型别有才有明确的语意.
(2). 重载的 operator&& 和 operator|| 无法模拟操作符默认的 骤死式语义[5].
(3). 你无法保证重载后操作符的参数求值次序(C++ Stand 保证了默认的 && 和 || 按从左到右求值).
2.自定义型别可以考虑提供到 bool 的转型操作来支持此操作符.
· bool operator||(const Carrot& lhs, const Carrot& rhs) X
- Carrot& Carrot::operator++()
(*this) += 1; // or other implement
return *this;
}
【注】1. 前置操作(prefix): ++carrot
- const Carrot Carrot::operator++(int)
Carrot oldValue = *this;
++(*this);
return oldValue;
}
【注】1. 后置操作(postfix): carrot++ 调用时编译器自动生成一个 0 作为参数.
2. 为了禁止 carrot++++ 操作, 返回值应为 const.
3. 从实现和参数可以看出,后置操作的效率远远低于前置操作, 所以如非必要尽量使用前置操作.
4. 为了保证递增行为一致, 并便于维护后置操作最好使用前置操作来完成递增行为.
· Carrot& Carrot::operator--()
· const Carrot Carrot::operator--(int)
- const Carrot operator,(const Carrot& lhs, const Carrot& rhs) X
...
}
【注】1. 基于以下原因, 你应该避免重载 operator, :
(1). 即使没有重载, 默认,号操作符也可以施用于自定义型别上.
(2). C++ 保证 , 操作符的求值是从左到右的, 而重载后无法保证此行为.
- const PMFC Carrot::operator->*(ReturnType (T::*pmf)()) const
- const Carrot* Carrot::operator&() const X
...
}
【注】1. 你应该尽量避免重载取地址操作符.
- Coca& Carrot::operator*()
...
}
【注】1. 重载提领操作符应提供 const 版和非 const 版.
· const Coca& Carrot::operator*() const
- Coca* Carrot::operator->()
...
}
【注】1. 重载 operator-> 应提供 const 版和非 const 版.
2. operator-> 的返回值必须为一个指针或可以应用 -> 操作的类型.
3. 如果 operator-> 的返回的不是一个指针, C++ 会继续在返回类型上应用 -> 直到得到一个指针为止.
· const Coca* Carrot::operator->() const
- Coca& Carrot::operator[](KeyType index)
...
}
【注】1. operator[] 应提供 const 版本和非 const 版.
2. KeyType 可以为任意类型, 但通常为 int 型.
· const Coca& Carrot::operator[](KeyType index) const
- AnyType Carrot::operator()(...)
...
}
【注】1. operator () 重载的是函数运算符, 改变表达式优先级的 () 是不能重载的.
2. 重载了 operator() 的对象被成为仿函数, 使用方法和函数指针类似.
- static void* Carrot::operator new(size_t size, ...)
if(size == 0)
size = 1;
while(1)
{
... // allocate memery
if(allocate sucessfull)
return ... // return the pointer.
new_handler curhandler = set_new_handler(0);
set_new_handler(curhandler); // get current new handler
if(curhandler == 0)
(*curhandler)();
else
throw std::bad_alloc();
}
【注】1. 参考 重载 operator new.
· static void* Carrot::operator new[](size_t size, ...)
- static void Carrot::operator delete(void * memory, size_t size)
if(memorty == NULL)
return;
...
}
【注】1. 参考 重载 operator new.
· static void Carrot::operator delete[](void * momery, size_t size)
▲注:
inline void *__cdecl operator new(size_t, void *_Where) _THROW0()
{ // construct array with placement at _Where
return (_Where);
}
struct nothrow_t
{ // placement new tag type to suppress exceptions
};
extern const nothrow_t nothrow; // constant for placement new tag
...
void *__cdecl operator new(size_t, const std::nothrow_t&)
_THROW0();
if( (p != NULL) && strlen(p) < 10 )...
如果 p == NULL, strlen 就不会被调用. 但是重载的 operator&& 和 operator|| 是作为一个函数, 要在所有的参数求值完成后才会调用此操作符. ←
注:这里又查了重载强制类型转换的函数
类型转换函数
类型转换是将一种类型的值映射为另一种类型的值,类型转换分隐式类型转换和强制类型转换两种。
类型转换函数是类中的一个非静态成员函数,可以看成是运算符重载的一种特殊情况。
为类X定义类型转换函数的格式为:
operator <类型>( ){ … }
这个转换函数定义了类X到<类型>之间的映射关系。
定义转换函数时应注意如下几点:
(1) 转换函数是用户定义的成员函数,但它要是非静态的。
(2) 由于转换函数的函数名是类型转换的目标类型,因此,不必再为它指定返回值。另外转换函数是将本类型的数值或变量转换为其他的类型,因此也不必带参数。
(3) 转换函数也不能定义为友元函数,但转换函数可以被派生类所继承,也可将其声明为虚函数。
例13.11类型转换函数举例
class CRational
{
public:
CRational(int d,int n){den=d;num=n;}
operator double(); //声明类型转换函数,它将Crational类型型转换为double类型
private:
int den,num;
};
CRational::operator double()
{
return double(den)/double(num);
}
void main(void)
{
CRational r(5,8);
double d=4.7,e,f;
d+=r; //A 将CRational类型隐式转换为double类型
e=CRational(r); //B 将CRational类型强制转换为double类型
f=(CRational)r; //C 强制转换的另一种形式
cout<<d<<'/t'<<e<<'/t'<<f<<endl;
}
▲参考资料:
2. Scott Meyers.Effective C++(2nd Edition)
3. Andrei Alexandrescu.Modern C++ Design
4. Robert B.Murray.C++ Strategies and Tactics
5. Scott Meyers.More Effective C++
6. John Lakos.Large-Scale C++ Software Design
7. Scott Meyers.Implementing operator->* for Smart Pointers