目录
友元机制和运算符重载
友元机制
友元函数
友元的作用
友元类
前置声明
友元类的注意事项
友元成员函数(类的某个成员函数 作为另一个类的友元)
运算符重载
运算符重载的作用
运算符重载的注意事项
运算符重载的实现
成员函数重载
友元函数重载
运算符重载规则
重载“=”运算符
重载"<"、">"运算符
重载"++"运算符
先自加再使用
先使用后自加
重载"+"运算符
重载"[ ]"运算符
重载"->"运算符
流运算符重载
重载函数调用运算符"()"(了解)
类型转换运算符(详解)
以bool类型转换作为引入
类型转换运算符详解
补充(内置类)
实例(内置类实现C++链表封装)
友元机制和运算符重载
友元机制
友元函数
比如下面这种情况:在func中每次打印num都需要调用get_num函数,函数的调用和销毁浪费了大量的运行时间(即函数不是类的一部分,但又需要频繁地访问类的数据成员)
因此C++提出了友元的概念
什么样的函数为友元函数?
函数不是类的一部分,但又需要频繁地访问类的数据成员
友元的作用
友元的作用:类的非成员函数可以直接访问类的非公有成员变量,省去函数的调用和访回过程。从而提高了程序的运行效率(即减少了类型和安全性检查及调用的时间开销)
友元类
前置声明
前置声明:只是单纯声明有这个类,但是不知道这个类的具体构造;
只可以利用类型名声明指针和引用变量,不能实例并访问类的内部构造;
若需要利用指针或引用调用前置类型的接口,必须按照声明和实现分离的方式进行编码。(后置实现)
注意:注意类声明的顺序;
友元类的注意事项
友元成员函数(类的某个成员函数 作为另一个类的友元)
友元成员函数:即让当前类的某个函数成为另一个类的友元函数,这样就可以在当前类的这个函数中访问另一个类的私有成员
注意事项:通过前置声明使用类的方法必须在前置类定义之后初始化,不能互为友元成员函数
class Room;//向前声明 只能说明类名称
class goodGay
{
public:
void visiting01(Room &room);
void visiting02(Room &room);
};
class Room
{
friend void goodGay::visiting02(Room &room);
private:
string bedRoom;//卧室
public:
string setingRoom;//客厅
public:
Room(string bedRoom, string setingRoom)
{
this‐>bedRoom = bedRoom;
this‐>setingRoom = setingRoom;
}
};
int main(int argc, char *argv[])
{
Room room("吴维的卧室","吴维的客厅");
goodGay ob;
ob.visiting01(room);
ob.visiting02(room);
return 0;
}
void goodGay::visiting01(Room &room)
{
cout<<"翰文访问了"<<room.setingRoom<<endl;
//cout<<"翰文访问了"<<room.bedRoom<<endl;
}
void goodGay::visiting02(Room &room)
{
cout<<"好基友张三访问了"<<room.setingRoom<<endl;
cout<<"好基友张三访问了"<<room.bedRoom<<endl;
}
运算符重载
可以通过函数参数的不同可以实现一个运算符任意方法的重载
运算符重载的作用
- 可以提高程序的可读性
- 体现了C++的可扩充性
- 运算符重载仅仅只是语法上的方便,它是另一种函数调用的方式
- 运算符重载,本质上是函数重载
运算符重载的注意事项
不要滥用重载、因为它只是语法上的方便,所以只有在涉及的代码更容易写、尤其是更易读时才有必要重载
运算符重载的实现
成员函数重载
友元函数重载
运算符重载规则
- 运算符重载不允许发明新的运算符
- 不能改变运算符操作对象的个数
- 运算符被重载后,其优先级和结合性不会改变
- 不能重载的运算符
- 成员函数重载和友元函数重载的选择
- 一般情况下,单目运算符最好重载为类的成员函数;双目运算符则最好重载为类的友元函数。
- 以下一些双目运算符不能重载为类的友元函数:=、()、[ ]、->。
- 类型转换运算符只能以成员函数方式重载
- 流运算符只能以友元的方式重载
重载“=”运算符
MyString &MyString::operator=(MyString ob)
{
//str2 = str1;
if(this->str != NULL)
{
delete [] this->str;
this->str = NULL;
}
this->size = ob.size;
this->str = new char[this->size+1];
memset(this->str, 0, this->size+1);
strcpy(this->str, ob.str);
return *this;
}
MyString &MyString::operator=(char *str)
{
//str2 = str1;
if(this->str != NULL)
{
delete [] this->str;
this->str = NULL;
}
this->size = strlen(str);
this->str = new char[this->size+1];
memset(this->str, 0, this->size+1);
strcpy(this->str, str);
return *this;
}
类的赋值运算符 “=” 只能重载为成员函数,而不能把它重载为友元函数,具体原因见:赋值运算符"="的重载https://blog.csdn.net/aaqian1/article/details/86423858
重载"<"、">"运算符
bool MyString::operator>(MyString ob)
{
if(str==NULL || ob.str == NULL)
{
exit(-1);
}
if(strcmp(this->str, ob.str) > 0)
{
return true;
}
return false;
}
bool MyString::operator>(char *str)
{
if(this->str==NULL || str == NULL)
{
exit(-1);
}
if(strcmp(this->str, str) > 0)
{
return true;
}
return false;
}
bool MyString::operator<(char *str)
{
if(this->str==NULL || str == NULL)
{
_Exit(-1);
}
if(strcmp(this->str,str)<0)
{
return true;
}
return false;
}
bool operator<(const MyString &ob1, const MyString &ob2)
{
if(ob1.str==NULL || ob2.str == NULL)
{
_Exit(-1);
}
if(strcmp(ob1.str,ob2.str)<0)
{
return true;
}
return false;
}
运行结果:
重载"++"运算符
实例:Integer类(友元和成员函数重载方式)
先自加再使用
成员函数重载
友元函数重载
先使用后自加
成员函数重载
友元函数重载
重载"+"运算符
MyString operator+(const MyString &ob1,const MyString &ob2)
{
MyString tmp;
tmp.size = ob1.size+ob2.size;
tmp.str = new char[tmp.size+1];
memset(tmp.str, 0, tmp.size+1);
strcpy(tmp.str, ob1.str);
strcat(tmp.str, ob2.str);
return tmp;
}
MyString MyString::operator+(char *str)
{
MyString tmp;
tmp.size = size+strlen(str);
tmp.str = new char[tmp.size+1];
memset(tmp.str, 0, tmp.size+1);
strcpy(tmp.str, this->str);
strcat(tmp.str, str);
return tmp;
}
重载"[ ]"运算符
char& MyString::operator[](int pos)
{
if(pos<0 || pos>=size)
{
cout<<"元素位置不合法"<<endl;
exit(-1);
}
return str[pos];
}
重载"->"运算符
->重载实现访问类中内置成员类里的某个成员
如下所示LinkIterator类和Node类同属于Link类的内置类,在main函数中实例化一个Link类对象指针,如果没有重载"->",对象指针将无法访问Node类中的私有成员m_num和next。但是由于Link类中有一个Node类的成员对象指针m_p。因此通过在Link类中重载"->",返回m_p,那么在main函数中的Link类对象指针就可以通过"->"访问到m_p中的m_num和next。
流运算符重载
如果采用成员函数的方法,由于是cout的成员,因此不能当前在类中实现重载。所以只能采用第二种方法。
流运算符>为内部类,不能通过类内成员函数重载的方法来实现,只能通过友元函数的方法来实现。
//全局函数实现 <<重载
ostream& operator<<(ostream &out, MyString ob)
{
out<<ob.str;
return out;
}
//全局函数实现 >>重载
istream& operator>>(istream &in, MyString &ob)
{
char buf[1024]="";
cin>>buf;
if(ob.str != NULL)//ob已经有字符串
{
delete [] ob.str;
ob.str = NULL;
}
ob.size = strlen(buf);
ob.str = new char[ob.size+1];
memset(ob.str, 0,ob.size+1);
strcpy(ob.str, buf);
return in;
}
重载函数调用运算符"()"(了解)
重载()运算符 一般用于 为算法 提供策略。
以重载()用于输出信息为例:
类型转换运算符(详解)
以bool类型转换作为引入
在MyString类中定义了bool类型转换函数,在main函数中实例化MyString对象的时候会隐式地将MyString类型转到bool的类型。
PS:类型的true还是false由bool类型转换构造函数中的具体返回值来确定
如果str1中的str不为空,则打印str中的内容:
输出结果如下:
类型转换运算符详解
类型转换运算符(conversion operator)是类的一种特殊成员函数,它负责将一个类类型的值转换成其他类型。类型转换函数的一般形式是:
operator type() const;
其中,type表示转换目标类型,除了void、数组或函数类型,但允许转换成指针(包括数组指针及函数指针)。类型转换运算符既没有显式的返回类型,也没有形参,而且必须定义为类的成员函数。类型转换运算符通常不应该改变待转换对象的内容,因此,类型转换运算符一般被定义成const成员。
转换构造函数和类型转换运算符,有时也被称作用户定义的类型转换(use-defined conversions)。
定义含有类型转换运算符的类
举一个简单的例子,令其表示0到255之间的一个整数:
class SmallInt
{
public:
SmallInt(int i = 0) :val(i)
{
if (i < 0 || i>255)
{
throw out_of_range("Bad SmallInt value");
}
}
operator int() const {return val;}
private:
size_t val;
};
这里的SmallInt类既定义了向类类型的转换,也定义了从类类型向其他类型的转换。其中,构造函数将算术类型的值转换成了SmallInt对象,而类型转换运算符将SmallInt对象转换成int:
SmallInt si;
si = 4; // 首先将4隐式地转换成SmallInt,然后调用SmallInt::operator=
si + 3; // 首先将si隐式地转换成int,然后执行整数的加法
尽管编译器一次只能执行一次用户定义的类型转换,但是隐式的用户定义类型转换可以置于一个标准(内置)类型转换之前或之后,并与其一起使用。因此,我们可以将任何算术类型传递给SmallInt的构造函数。类似的,我们也能使用类型转换运算符将一个SmallInt对象转换成int,然后再将所得的int转换成任何其他的算术类型:
// 内置类型转换将double实参转换成int
SamllInt si = 3.14; // 调用SmallInt(int)构造函数
// SmallInt的类型转换运算符将si转换成int
si + 3.14; // 内置类型转换将所得的int继续转换成double
在实践中,类很少提供类型转换运算符。在大多数情况下,如果类型转换自动发生,用户可能会感觉比较意外,而不是感觉受到了帮助。而这条经验法则存在一种例外的情况:对于类来说,定义向bool类型转换还是比较普遍的现象。
显示的类型转换运算符
C++11新标准引入了显示的类型转换运算符(explicit conversion operator):
class SmallInt
{
public:
// 编译器不会自动执行这一类型转换
explicit operator int() const {return val;}
// 其他成员与之前的版本一致
};
和显式的构造函数一样,编译器通常也不会将一个显式的类型转换运算符用于隐式类型转换:
SmallInt si = 3; // 正确:SmallInt的构造函数不是显式的
si + 3; // 错误:此处需要隐式的类型转换,但类的运算符是显式的
static_cast<int>(si) + 3; // 正确:显式地请求类型转换
类型转换参考如下
C++重载运算与类型转换https://blog.csdn.net/weixin_43918519/article/details/123933954?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-2-123933954-blog-100067470.235%5Ev38%5Epc_relevant_anti_t3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~BlogCommendFromBaidu~Rate-2-123933954-blog-100067470.235%5Ev38%5Epc_relevant_anti_t3&utm_relevant_index=3
关于operator bool () 和bool operator ==()https://blog.csdn.net/znzxc/article/details/80385995
C++中Operator类型强制转换成员函数解析https://blog.csdn.net/zhangzheng_1986/article/details/81080407
补充(内置类)
必须使用内置类的封装才能接管++、--运算符,当传统类型已经不足够进行操作的时候,必须要内置类进行封装
实例(内置类实现C++链表封装)
#include <iostream>
using namespace std;
class Link
{
public:
class Node
{
public:
Node(int num) : m_num(num), next(nullptr)
{
}
int m_num;
Node *next;
friend ostream& operator<<(ostream &out, const Link::Node node);
};
class LinkIterator;
typedef LinkIterator iterator;
// typedef Node * iterator;
class LinkIterator
{
public:
LinkIterator(Node *p) : m_p(p)
{
}
LinkIterator operator++()
{
m_p = m_p->next;
return *this;
}
LinkIterator operator++(int num)
{
LinkIterator temp(this->m_p);
this->m_p = m_p->next;
return temp;
}
Node *operator->()
{
return m_p;
}
bool operator!=(const LinkIterator &other)
{
return m_p != other.m_p;
}
bool operator==(const LinkIterator &other)
{
return this->m_p == other.m_p;
}
LinkIterator & operator+(int index)
{
for(int i = 0; i < index; i++)
{
m_p = m_p->next;
}
return *this;
}
Node operator*()
{
return *m_p;
}
private:
Node *m_p;
};
iterator begin()
{
return LinkIterator(m_head);
}
iterator end()
{
return LinkIterator(nullptr);
}
Link(Node *head = nullptr) : m_head(head)
{
}
~Link()
{
Node *temp = m_head;
while (m_head != nullptr)
{
temp = m_head;
m_head = m_head->next;
delete temp;
}
m_head = nullptr;
}
void insert_head(Node *newnode)
{
newnode->next = m_head;
m_head = newnode;
}
void insert_tail(Node *newnode)
{
Node *temp = m_head;
if (temp == nullptr)
{
newnode->next = nullptr;
m_head = newnode;
}
else
{
while (temp->next != nullptr)
{
temp = temp->next;
}
temp->next = newnode;
newnode->next = nullptr;
}
}
bool insert_mid(Node *newnode, int index)
{
Node *temp = m_head;
if (temp == nullptr)
{
return false;
}
while (temp != nullptr)
{
if (temp->m_num == index)
{
newnode->next = temp->next;
temp->next = newnode;
return true;
}
temp = temp->next;
}
return false;
}
void insert_mid(iterator it, Node *newnode)
{
newnode->next = it->next;
it->next = newnode;
}
iterator find(int index)
{
for (auto it = this->begin(); it != this->end(); ++it)
{
if (it->m_num == index)
{
return it;
}
}
return this->end();
}
bool delete_node(Node *node)
{
Node *temp = m_head;
if (temp == nullptr)
{
return false;
}
if (m_head->m_num == node->m_num)
{
m_head = m_head->next;
free(temp);
temp = nullptr;
return true;
}
else
{
Node *p = temp;
temp = temp->next;
while (temp != nullptr)
{
if (temp->m_num == node->m_num)
{
p->next = temp->next;
free(temp);
temp = nullptr;
return true;
}
p = temp;
temp = temp->next;
}
return false;
}
}
void display()
{
Node *temp = m_head;
while (temp != nullptr)
{
cout << temp->m_num << " ";
temp = temp->next;
}
cout << "\n";
}
private:
Node *m_head;
};
ostream& operator<<(ostream &out, const Link::Node node)
{
out << node.m_num;
return out;
}
int main(int argc, char **argv)
{
Link::Node p(5);
Link link;
for (int i = 0; i < 5; i++)
{
link.insert_tail(new Link::Node(i));
}
link.insert_mid(new Link::Node(5), 3);
link.insert_head(new Link::Node(6));
link.delete_node(new Link::Node(4));
// link.display();
for (auto it = link.begin(); it != link.end(); ++it)
{
//cout << it->m_num << endl;
cout << *it << endl;
}
auto it = link.find(6);
if (it == link.end())
{
cout << "not find" << endl;
}
else
{
cout << it->m_num << endl;
}
it = it + 3;
cout << it->m_num << endl;
string s1 = "hello world";
for(auto it = s1.rbegin(); it != s1.rend(); it++)
{
cout << *it << endl;
}
return 0;
}