C++ operator的细节
运算符重载是C++重要的特性之一,可以是代码看起来更加自然,使编写者在编写代码时操作更加方便。
运算符重载的限制:
重载后运算符的操作数至少有一个是用户定义类型。
不能违反原来操作数的语法规则。
不能创建新的运算符。
不能重载 sizeof 运算符,. 运算符,.* 运算符,:: 运算符, ?: 运算符,RTTI类型运算符。
= 运算符,() 运算符,[] 运算符,-> 运算符只能被成员函数重载。
前缀与后缀
一元运算符往往有前缀和后缀两种意义,那么他们在重载时就需分别进行重载。
前缀运算符重载规则:参数列表为空。
后缀运算符重载规则:参数列表有一个匿名的int参数。
//main.cpp
# include <iostream>
class C
{
public:
explicit C(int i = 0) :m_i(i) {}
~C() {}
C operator ++()//前自增
{
++m_i;//增加
return *this;//返回现在的的状态
}
C operator ++(int)//后自增
{
C tmp(*this);//备份现在的状态
++m_i;//增加
return tmp;//返回先前的的状态
}
C operator +()//正号
{
C tmp(*this);//拷贝当前状态
tmp.m_i = +tmp.m_i;//取正
return tmp;//返回临时对象
}
C operator -()//负号
{
C tmp(*this);//拷贝当前状态
tmp.m_i = -tmp.m_i;//取负
return tmp;//返回临时对象
}
friend std::ostream & operator <<(std::ostream & os, const C & other)
{
os << other.m_i;
return os;
}
private:
int m_i;
};
int main()
{
C obj(9);
std::cout << ++obj << std::endl;
std::cout << obj++ << std::endl;
std::cout << -obj << std::endl;
std::cout << +obj << std::endl;
return 0;
/*
10
10
-11
11
*/
}
new与delete
内存管理运算符通常不需要重载。但如果需要检测代码中的内存错误,优化性能,获得内存使用的统计等,可以重载内存管理运算符实现功能。
new运算符和new[]运算符重载规则:参数个数任意,但第一个参数必须是size_t类型的,返回值必须是void*类型。
delete运算符和delete[]运算符重载规则:只允许有一个参数,且是void*类型,返回值必须是void类型。
//main.cpp
# include <iostream>
# include <list>
using namespace std;
//定义内存信息类
class MemInfo
{
public:
MemInfo(void *address, size_t size, const char *file, size_t line)
{
m_address = address;
m_size = size;
strcpy(m_file, file);
m_line = line;
}
~MemInfo() {}
void *GetMemoryAddress()const
{
return m_address;
}
friend ostream & operator <<(ostream & os, const MemInfo & meminfo)
{
os << "0x" << meminfo.m_address << " ";
os << meminfo.m_size << "Byte" << " ";
os << meminfo.m_file << " ";
os << meminfo.m_line;
return os;
}
private:
void *m_address;
size_t m_size;
char m_file[64];
size_t m_line;
};
//创建全局内存分配表
list<MemInfo> MemAllocList;
//重载内存管理运算符
void *operator new(size_t size, const char *file, size_t line)
{
void *p = NULL;
p = malloc(size);
//添加内存信息到内存分配表中
MemAllocList.push_back(MemInfo(p, size, file, line));
return p;
}
void *operator new[](size_t size, const char *file, size_t line)
{
return operator new(size, file, line);
}
void operator delete(void *p)
{
for (list<MemInfo>::iterator MemItr = MemAllocList.begin(); MemItr != MemAllocList.end() && (!MemAllocList.empty()); MemItr++)
{
if (MemItr->GetMemoryAddress() == p)
{
//删除内存分配表中的此内存信息
MemAllocList.erase(MemItr);
break;
}
}
free(p);
}
void operator delete[](void *p)
{
operator delete(p);
}
//重载内存分配表的流输出运算符
ostream & operator <<(ostream & os, const list<MemInfo> & memalloclist)
{
for (list<MemInfo>::const_iterator MemItr = memalloclist.begin(); MemItr != memalloclist.end(); MemItr++)
{
os<<*MemItr<<endl;
}
return os;
}
//创建宏进行替换
#define new new(__FILE__,__LINE__)
int main()
{
int *var_1 = new int;
double *var_2 = new double[10];
char *var_3 = new char('4');
cout << "Before delete:" << endl;
cout << MemAllocList;
delete []var_2;
cout << "After delete:" << endl;
cout << MemAllocList;
/*
Before delete:
0x014ACA70 4Byte d:\projects\c++\test\test\main.cpp 87
0x014A79F8 80Byte d:\projects\c++\test\test\main.cpp 88
0x014AE710 1Byte d:\projects\c++\test\test\main.cpp 89
After delete:
0x014ACA70 4Byte d:\projects\c++\test\test\main.cpp 87
0x014AE710 1Byte d:\projects\c++\test\test\main.cpp 89
*/
return 0;
}
重载转换函数也用operator
在重载转函数时,用operator关键字进行重载。重载转换函数必须为类成员函数,返回值不需要与重载转换函数的类型一致,因为C++会根据转换函数的类型进行隐式转换。
重载规则:不能指定返回类型,并且形参表必须为空。
//main.cpp
# include <iostream>
class C
{
public:
explicit C(int i = 0) :m_i(i) {}
~C() {}
operator int()
{
return m_i;
}
operator char()
{
return m_i;
}
friend std::ostream & operator <<(std::ostream & os,const C & other)
{
os << other.m_i;
return os;
}
private:
int m_i;
};
int main()
{
C obj(65);
std::cout << int(obj) << std::endl;
std::cout << char(obj) << std::endl;
return 0;
/*
65
A
*/
}
何时需要friend
当两不同类型的操作数进行运算时,重载的运算符函数总是后一个操作数类型的友元函数。因为私有的数据,普通函数无法访问,所以必须定义为友元函数才能访问。
重载规则:在所需类型中定义为友元函数。
//main.cpp
# include <iostream>
class C
{
public:
explicit C(int i = 0) :m_i(i) {}
~C() {}
friend C operator +(int i,const C & obj)
{
C tmp;
tmp.m_i = i + obj.m_i;
return tmp;
}
friend std::ostream & operator <<(std::ostream & os,const C & other)
{
os << other.m_i;
return os;
}
private:
int m_i;
};
int main()
{
C obj_1(0);
C obj_2(1);
obj_1 = 2 + obj_2;
std::cout << obj_1 << std::endl;
return 0;
/*
3
*/
}
何时需要返回引用类型
当需要重载运算符实现链式调用时,就需要将重载运算符的返回值设置为引用类型。
重载规则:设置返回值为引用类型。
//main.cpp
# include <iostream>
class C
{
public:
explicit C(int i = 0) :m_i(i) {}
~C() {}
C operator =(const C & obj)
{
if (&obj != this)
{
m_i = obj.m_i;
}
return *this;;
}
friend std::ostream & operator <<(std::ostream & os, const C & other)
{
os << other.m_i;
return os;
}
private:
int m_i;
};
int main()
{
C obj_1(0);
C obj_2(1);
C obj_3(2);
obj_1 = obj_2 = obj_3;//obj_1.operator=(obj_2.operator=(obj_3));
std::cout << obj_1 << std::endl;
std::cout << obj_2 << std::endl;
std::cout << obj_3 << std::endl;
return 0;
/*
2
2
2
*/
}