运算符重载
一、友元机制
1.友元介绍
·友元是一种允许非类成员函数访问类的非公有成员的一种机制
·可以把一个函数指定为类的友元,也可以把整个类指定为另一个类的友元。
友元函数
友元类
2.友元函数
·友元函数在类作用域外定义,但它需要在类体中进行说明
·为了与该类的成员函数加以区别,定义的方式是在类中用关键字friend说明该函数,格式如下:
friend 类型 友元函数名(参数表)
友元的作用在于提高程序的运行效率
友元函数的注意事项:
·友元函数不是类的成员函数,在函数体中访问对象的成员,必须用对象名加运算符“.”加对象成员名。但友元函数可以访问类中的所有成员(公有的、私有的、保护的),一般函数只能访问类中的公有成员。
·友元函数不受类中的访问权限关键字限制,可以把它放在类的公有、私有、保护部分,但结果一样。
·某类的友元函数的作用域并非该类作用域。如果连友元函数不是另一类的成员函数,则其作用域为另一类的作用域,否则与一般函数相同。
·友元函数破坏了面向对象程序类的封装性,所有友元函数如不是为了必须使用则尽可能少用。或者用其他手段保护封装性。
3.友元类
友元类的注意事项:
·友元关系是单向的
·友元关系不能被传递
·友元关系不能被继承
代码示例:
String.h;
#ifndef _STRING_H_
#define _STRING_H_
class String
{
public:
friend class StringTool;
friend void print(const String &s1); //友元函数
String();
String(char *str);
~String();
void Display();
private:
char *str_;
};
#endif
String.cpp:
#include <iostream>
#include "String.h"
using namespace std;
String::String()
{
cout << "default constructor String!" << endl;
}
String::String(char *str)
{
cout << "constructor String" << endl;
int len = strlen(str) + 1;
str_ = new char(len);
memset(str_, 0, len);
strcpy(str_, str);
}
String :: ~String()
{
cout << "destory String" << endl;
}
void String::Display()
{
cout << str_ << endl;
}
void print(const String& s1)//友元函数
{
cout << s1.str_ << endl;
}
StringTool.h:
#ifndef _STRINGTOOL_H_
#define _STRINGTOOL_H_
#include "String.h"
class StringTool
{
public:
void mystrcat(String& s1, String& s);
};
#endif
StringTool.cpp:
#include <iostream>
#include <string.h>
#include "StringTool.h"
void StringTool::mystrcat(String &s1, String &s)
{
strcat(s1.str_, s.str_);
}
main.c:
#include <iostream>
#include "String.h"
#include "StringTool.h"
using namespace std;
int main()
{
String s1("hello");
print(s1);
//String s2("world");
//StringTool st;
//st.mystrcat(s1, s2);//调用友元类
return 0;
}
运行结果:
二、运算符重载
1.运算符重载介绍
·运算符重载允许把标准运算符(如+、—、*、/、<、>等)应用于自定义数据类型的对象
运算符重载的作用:
·直观自然,可以提高程序的可读性
·体现了C++的可扩充性
·运算符重载仅仅只是为了语法上的方便,它是另一种函数调用方式
·运算符重载,本质上是函数重载
运算符重载的注意事项:
·不要滥用重载,本质上是函数重载
2.运算符重载的实现
(1)成员函数重载
· 成员函数原型格式:
函数类型 operator 运算符(参数表);
·成员函数定义的格式:
函数类型 类名 :: operator 运算符(参数表)
{
函数体;
}
(2)友元函数重载
·友元函数原型的格式:
friend 函数类型 operator运算符(参数表);
·友元函数定义的格式:
函数类型 函数类型 类名::operator 运算符(参数表)
{
函数体;
}
3.运算符重载规则
·运算符重载不允许发明新的运算符
·不能改变运算符操作对象的个数
·运算符被重载后,其优先级和结合性不会改变
·不能被重载的运算符
作用域解析运算符 ::
条件运算符 ?:
直接成员访问运算符 .
类成员指针引用的运算符 .*
sizeof运算符 sizeof
·成员函数重载和友元函数重载的选择
一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函
数
以下一些双目运算符不能重载为类的友元函数:= ()、[]、->
类型转换运算符只能以成员函数方式重载
流运算符只能以友元的方式重载
4.++运算符的重载
(1)成员函数重载方式
(2)友元函数重载方式
代码示例:
Integer.h:
#ifndef _INTEGER_H_
#define _INTEGER_H_
class Integer
{
public:
Integer();
Integer(int n);
~Integer();
void Display() const;
Integer& operator++();
Integer operator++(int n);
// friend Integer& operator++(Integer &i); //友元函数重载方式
// friend Integer operator++(Integer &i,int n); //友元函数重载方式
operator int();
private:
int n_;
};
#endif
Integer.cpp:
#include "Integer.h"
#include <iostream>
using namespace std;
Integer :: Integer()
{
}
Integer :: Integer(int n) : n_(n)
{
}
Integer :: ~Integer()
{
}
void Integer :: Display() const
{
cout << n_ << endl;
}
Integer& Integer :: operator++()
{
cout << "++i" << endl;
++n_;
return *this;
}
Integer Integer :: operator++(int n)
{
cout << "i++" << endl;
Integer tmp(n_);
n_++;
return tmp;
}
#if 0
Integer& operator++(Integer& i)
{
cout << "friend ++i" << endl;
++i.n_;
return i;
}
Integer operator++(Integer& i,int n)
{
cout << "friend i++" << endl;
Integer temp(i.n_);
i.n_++;
return temp;
}
#endif
Integer :: operator int()
{
return n_;
}
Main.c:
#include "Integer.h"
#include <iostream>
using namespace std;
Integer :: Integer()
{
}
Integer :: Integer(int n) : n_(n)
{
}
Integer :: ~Integer()
{
}
void Integer :: Display() const
{
cout << n_ << endl;
}
Integer& Integer :: operator++()
{
cout << "++i" << endl;
++n_;
return *this;
}
Integer Integer :: operator++(int n)
{
cout << "i++" << endl;
Integer tmp(n_);
n_++;
return tmp;
}
#if 0
Integer& operator++(Integer& i)
{
cout << "friend ++i" << endl;
++i.n_;
return i;
}
Integer operator++(Integer& i,int n)
{
cout << "friend i++" << endl;
Integer temp(i.n_);
i.n_++;
return temp;
}
#endif
Integer :: operator int()
{
return n_;
}
运行结果请自行实践~
5.!运算符重载
成员函数原型:
bool operator!() const;
成员函数定义:
bool String :: operator!() const
{
return strlen(str_) ! = 0;
}
6.赋值运算符重载
成员函数原型:
String& operator = (const String& other);
成员函数定义:
String& String :: operator = (const String& other)
{
if(this == &other)
{
return * this;
}
int len = strlen(other.str_) + 1;
delete[] str_;
str_ = new char[len];
memset(str_,0,len);
strcpy(str_,other.str_);
}
7.[]运算符重载
成员函数原型:
(1)char& operator[](unsigned int index);
(2)const char& operator[](unsigned int index) const;
成员函数定义:
(1)char& String :: operator[](unsigned int index)
{
cout << "no const" << endl;
//return str_[index];
return const_cast<char&>(static_cast<const String&>(*this)[index]);
}
(2)const char& String :: operator[](unsigned int index) const
{
cout << "const[]" << endl;
return str_[index];
}
8.+运算符重载
成员函数原型:
friend String operator+(const String &s1,const String &s2);
成员函数定义:
String operator+(const String& s1,const String& s2)
{
#if 0
int len = strlen(s1.str_) + strlen(s2.str_) + 1;
char * newstr = new char[len];
memset(newstr,0,len);
strcpy(newstr,s1.str_);
strcat(newstr,s2.str_);
String temp(newstr);
delete newstr;
#endif //实现+=后可以直接使用下面几行代码
String temp(s1);
temp += s2;
return temp;
}
9.+=运算符重载
成员函数原型:
String& operator+=(const String& other);
成员函数定义:
String& String :: operator+=(const String& other)
{
int len = strlen(str_) + strlen(other.str_) + 1;
char * newstr = new char[len];
memset(newstr,0,len);
strcpy(newstr,str_);
strcat(newstr,other.str_);
delete[] str_;
str_ = newstr;
return * this;
}
10.流运算符重载
·为什么一定要使用友元函数进行重载?
如果是重载双目操作符(即为类的成员函数),就只要设置一个参数作为右侧运算量,而左侧运算量就是对象本身
而 >> 或<<左侧运算量是 cin或cout而不是对象本身,所以不满足后面一点,就只能申明为友元函数了
成员函数原型:
(1)friend ostream& operator<<(ostream& out,const String& s);
(2)friend istream& operator>>(istream& in,String& s);
成员函数定义:
(1)ostream& operator<<(ostream& out,const String& s)
{
out << s.str_;
return out;
}
(2)istream& operator>>(istream& in,String& s)
{
char buffer[1024];
in >> buffer;
int len = strlen(buffer) + 1;
delete[] s.str_;
s.str_ = new char[len];
strcpy(s.str_,buffer);
return in;
}
11.类型转换运算符重载
·必须是成员函数,不能是友元函数
·没有参数(操作数是什么?)
·不能指定返回类型(其实已经指定了)
·函数原型:operator 类型名();
Integer :: operator int()
{
return n_;
}
int main(void)
{
Integer n(100);
n = 200;
n.Display();
int sum = add(n,100);
cout << sum << endl;
int x = n;
int v = static cast<int>(n);
return 0;
}
12.->运算符重载
#include <iostream>
using namespace std;
class DBHelper
{
public:
DBHelper()
{
cout<<"DB ..."<<endl;
}
~DBHelper()
{
cout<<"~DB ..."<<endl;
}
void Open()
{
cout<<"Open ..."<<endl;
}
void Close()
{
cout<<"Close ..."<<endl;
}
void Query()
{
cout<<"Query ..."<<endl;
}
};
class DB
{
public:
DB()
{
db_ = new DBHelper;
}
~DB()
{
delete db_;
}
DBHelper* operator->()
{
return db_;
}
private:
DBHelper* db_;
};
int main(void)
{
DB db;
db->Open();
db->Query();
db->Close();
return 0;
}
13.new/delete
(1)operator new
(1) 只分配所要求的空间,不调用相关对象的构造函数。当无法满足所要求分配的空间时,则 ->如果有new_handler,则调用new_handler,否则
->如果没要求不抛出异常(以nothrow参数表达),则执行bad_alloc异常,否则
->返回0
(2) 可以被重载
(3) 重载时,返回类型必须声明为void*
(4) 重载时,第一个参数类型必须为表达要求分配空间的大小(字节),类型为size_t
(5) 重载时,可以带其它参数
void * operator new(size_t size)
{
cout <<“void * operator new(size_t size size)” << endl;
void *p = malloc(size);
return p;
}
void operator delete(void *p)
{
cout <<“void operator delete(void *p)” << endl;
free(p);
}
(2)new operator
new operator
(1)调用operator new分配足够的空间,并调用相关对象的构造函数
(2)不可以被重载
(3)placement new(不分配内存+构造函数的调用)