运算符重载允许把标准运算符(如+、—、*、/、<、>等)应用于自定义数据类型的对象。直观自然,可以提高程序的可读性,体现了C++的可扩充性。运算符重载仅仅只是语法上的方便,它是另一种函数调用的方式。运算符重载,本质上是函数重载,所以不要滥用重载、因为它只是语法上的方便,所以只有在涉及的代码更容易写、尤其是更易读时才有必要重载。
运算符重载有两种方式,一种是重载为成员函数,函数原型如下。
返回值类型 operator 运算符(参数表);
还可以重载为友元函数的方式,函数原型如下。
friend 返回值类型 operator 运算符(参数表);
可重载的运算符有一元运算符和二元运算符,一元运算符有一个参数,二元运算符有两个参数,对应于函数调用的参数。重载为成员函数时,this指针默认为第一个参数,而重载为友元时需要显示表示所有参数。请看下面这个例子。
#include <iostream>
using namespace std;
class Integer{
friend ostream& operator<<(ostream& out, const Integer& i);
public:
Integer(int val):val_(val){}
Integer& operator++(){
val_++;
return *this;
}
Integer operator++(int i){
Integer temp(val_);
val_++;
return temp;
}
operator int(){
return val_;
}
private:
int val_;
};
ostream& operator<<(ostream& out, const Integer& i){
out<<i.val_;
return out;
}
int main(int argc, char const *argv[])
{
Integer i(2);
cout<<i++<<endl; /* cout<<i.operator++()<<endl; */
cout<<++i<<endl; /* cout<<i.operator++(0)<<endl; */
int a = 1 + i;
cout<<a<<endl;
system("pause");
return 0;
}
这个例子想说明的是,第一,可以手动调用重载的运算符;第二,因为++,--运算符分为前置和后置,所以在重载运算符的时候,后置的++,--需要增加一个int参数,这个参数没什么用,只是为了区分是前置还是后置。并且,可以看出,由于后置的++,--为了满足语义需要创建一个临时对象,所以用前置++,--的效率要高一点。
此外,运算符重载有以下的注意点。
- 运算符重载不允许发明新的运算符。
- 不能改变运算符操作对象的个数。
- 运算符被重载后,其优先级和结合性不会改变。
- 以下运算符不能重载:::(作用域解析运算符),?:( 条件运算符),.(直接成员访问运算符),.*(类成员指针引用的运算符 ),sizeof(sizeof运算符 )。
- 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
- 以下一些双目运算符不能重载为类的友元函数:=、()、[]、->。
- 类型转换运算符只能以成员函数方式重载,没有参数,不能指定返回类型,函数原型如下。
operator 类型名();
- 流运算符只能以友元的方式重载。
运算符重载例子,String类的简单实现
String类需要重载的运算符为,=运算符,[]运算符重载,+运算符重载,+=运算符重载,<<运算符重载, > >运算符重载。下面是具体的代码。
#include <iostream>
#include <exception>
#include <string.h>
using namespace std;
class String{
friend ostream& operator<<(ostream& os, const String& s);
friend istream& operator>>(istream& is, const String& s);
friend String operator+(const String& s1, const String& s2);
public:
String(const char *str = ""){
str_ = new char[strlen(str) + 1];
strcpy(str_,str);
}
~String(){
delete[] str_;
}
char *str_;
String(const String& other){
delete[] str_;
str_ = new char[strlen(other.str_)+1];
strcpy(str_,other.str_);
}
String& operator=(const String& other){
if(this != &other){
delete[] str_;
str_ = new char[strlen(other.str_) + 1];
strcpy(str_,other.str_);
}
return *this;
}
String& operator=(const char *s){
delete[] str_;
str_ = new char[strlen(s) + 1];
strcpy(str_,s);
return *this;
}
String& operator+=(const String& other){
char *str = new char[strlen(str_) + strlen(other.str_) + 1];
strcpy(str,str_);
strcat(str,other.str_);
delete[] str_;
str_ = str;
return *this;
}
const char& operator[](int index) const{
if(index >= strlen(str_)){
throw out_of_range("index is out of range");
}
return str_[index];
}
char& operator[](int index){
const char& c = (static_cast<const String&>(*this))[index];
return const_cast<char &>(c);
}
};
ostream& operator<<(ostream& os, const String& s){
os<<s.str_;
return os;
}
istream& operator>>(istream& is, String& s){
char temp[1024];
is>>temp;
s = temp;
return is;
}
String operator+(const String& s1, const String& s2){
char * str = new char[strlen(s1.str_) + strlen(s2.str_) + 1];
strcpy(str,s1.str_);
strcat(str,s2.str_);
return String(str);
}
int main(int argc, char const *argv[])
{
String s1,s2;
cin>>s1>>s2;
s1 += s2;
for(int i=0;i<strlen(s1.str_);i++){
cout<<s1[i];
}
cout<<endl;
String s3 = s1 + "!!!";
cout<<s3;
system("pause");
return 0;
}
重载->运算符
这个运算符的重载有点奇怪,先看看下面的代码。
#include <iostream>
using namespace std;
class DB{
public:
DB(){cout<<"DB()"<<endl;}
~DB(){cout<<"~DB()"<<endl;}
void open(){cout<<"open()"<<endl;}
void close(){cout<<"close()"<<endl;}
void query(){cout<<"query()"<<endl;}
};
class DBHelper{
public:
DBHelper(){
db_ = new DB();
}
~DBHelper(){
delete db_;
}
DB* operator->(){
return db_;
}
private:
DB* db_;
};
int main(int argc, char const *argv[])
{
DBHelper db;
db->open();
db->close();
return 0;
}
虽然->运算符返回了一个DB指针,但是它的后面可以直接跟上一个函数调用,这让我感觉有点奇怪。
重载operator new、operator delete
先看下面一段代码。
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char const *argv[])
{
char buf[1024];
string *str = new(buf) string("hello");
cout<<&buf<<" "<<str<<endl;
str->~basic_string();
return 0;
}
一个new操作符执行的操作是,operator new + 调用构造函数。operator new的主要作用是分配内存,这个操作符是可以重载的,并且有几种形式。上面的这种用法是placement new,不分配内存,而是指定一个内存。从上面的代码可以看出,在这种情况下,需要手动调用析构函数,不然构造函数中分配的内存无法释放。operator new和operator delete可以重载为局部的,也可以重载为全局的。我们可以通过重载一个全局的operator new和delete来实现一个简单的内存追踪器。内存跟踪器的大体思路是,我们重载全局的operator new和delete,自己实现内存的分配,然后使用一个全局的map,每次有内存分配,就添加一条记录;每次有释放内存,就去掉对应的记录。这个有点像模板设计模式,通过自定义一些功能来实现一些额外的功能。下面是具体的代码。
#include <map>
#include <iostream>
class Tracer{
friend class Lock;
public:
bool ready_;
Tracer():lockCount_(0),ready_(true){}
~Tracer(){
ready_ = false;
dump();
}
void add(void *p,const char *file, unsigned int line){
if(lockCount_ > 0){
return;
}
Tracer::Lock lock(*this);
entryMap_.insert(std::make_pair(p,Entry(file,line)));
}
void remove(void *p){
if(lockCount_ > 0){
return;
}
Tracer::Lock lock(*this);
std::map<void*,Entry> :: const_iterator it = entryMap_.find(p);
if(it != entryMap_.end()){
entryMap_.erase(it);
}
}
void dump(){
if(entryMap_.size() > 0){
std::cout << "-----------------------Memory leak(s)-----------------------" << std::endl;
std::map<void*,Entry>::const_iterator it;
for(it = entryMap_.begin();it != entryMap_.end(); ++it){
std::cout << "0x" << std::hex << it->first << ":"
<< it->second.file_ << " line: " << std::dec << it->second.line_ << std::endl;
}
std::cout<<std::endl;
}
}
private:
class Entry{
public:
const char *file_;
unsigned int line_;
Entry(const char *file = NULL, unsigned int line = 0):file_(file),line_(line){}
};
class Lock{
private:
Tracer& tracer_;
public:
Lock(Tracer& tracer):tracer_(tracer){
tracer_.lock();
}
~Lock(){
tracer_.unlock();
}
};
unsigned int lockCount_;
std::map<void*,Entry> entryMap_;
void lock(){
lockCount_++;
}
void unlock(){
lockCount_--;
}
};
Tracer gTracer;
void* operator new(size_t size, const char * file, unsigned int line){
if(size == 0){
size = 1;
}
void *p = malloc(size);
if(gTracer.ready_ )
gTracer.add(p,file,line);
return p;
}
void operator delete(void *p, const char*, unsigned int line){
if(gTracer.ready_ )
gTracer.remove(p);
free(p);
}
void* operator new(size_t size){
if(size == 0){
size = 1;
}
void *p = malloc(size);
if(gTracer.ready_ )
gTracer.add(p,"?",0);
return p;
}
void operator delete(void *p){
if(gTracer.ready_ )
gTracer.remove(p);
free(p);
}
void* operator new[](size_t size){
if(size == 0){
size = 1;
}
void *p = malloc(size);
if(gTracer.ready_ )
gTracer.add(p,"?",0);
return p;
}
void operator delete[](void *p){
if(gTracer.ready_ )
gTracer.remove(p);
free(p);
}
void* operator new[](size_t size, const char * file, unsigned int line){
if(size == 0){
size = 1;
}
void *p = malloc(size);
if(gTracer.ready_ )
gTracer.add(p,file,line);
return p;
}
void operator delete[](void *p, const char * file, unsigned int line){
if(gTracer.ready_ )
gTracer.remove(p);
free(p);
}
#define new new(__FILE__,__LINE__)
在构造函数中调用构造函数
class A{
public:
A(int val):val_(val){
cout<<val<<endl;
}
A(){
new(this) A(10); //如果使用A(10)只会在栈上创建一个临时对象
}
int val_;
};
网友评论