运算符重载(看完这篇彻底掌握重载问题)

运算符重载概述

什么是运算符重载

运算符重载:用同一个运算符完成不同的运算功能。
C++运算符重载的相关规定如下:

1.不能改变运算符的优先级
2.不能改变运算符的结合性。
3.默认参数不能和重载的运算符一起使用,也就是说,在设计运算符重载成员函数时不能使用默认函数。
4.不能改变运算符的操作数的个数。
5.不能创建新的运算符,只有已有运算符可以被重载
6.运算符作用于C++内部提供的数据类型时,原来含义保持不变

C++中可被重载的运算符:
在这里插入图片描述
C++中不能被重载的操作符
在这里插入图片描述

运算符重载函数的定义格式

运算符重载函数作为类的成员函数

函数类型   	operator	重载运算符(形参表)
{
	函数体;
}

运算符重载函数作为类的友元函数

friend	函数类型	operator	重载运算符(形参表)
{
	函数体;
}

其中,“函数类型”指出重载运算符的返回值类型,operator是定义运算符重载函数的关键词,“重载运算符”指出要重载的运算符名字,是C++中可重载的运算符,比如要重载加法运算符,这里直接写“+”即可,“形参表”指出重载运算符所需要的参数及其类型。
##重载单目运算符

重载“++”和“–”运算符

“++”和“–”重载运算符也有前缀和后缀两种运算符重载形式,以“++”重载运算符为例,其语法格式如下:

函数类型		operater		++()
函数类型		operater		++(int)

例:

#include<iostream>
using	namespace	std;
class MyClass2
{
	int n;
public:
	MyClass2(int i){ n = i; }
	int operator ++(){ n++; return n; }
	int operator ++(int){ n += 2; return n; }
	void display()
	{
		cout << "n=" << n << endl;
	}
};

void main()
{
	MyClass2 A(5), B(5);
	A++;
	++B;
	A.display();
	B.display();
	system("pause");
}

输出结果为:
在这里插入图片描述
注:在上述程序中,定义了前缀++和后缀++重载运算符,在main()中,语句A++调用了后缀重载运算符,语句++B调用前缀重载运算符。

重载->运算符

“->”运算符是成员访问运算符,这种单目运算符只能被重载为成员函数,一般成员访问运算符的时运格式如下:

对象->成员

成员访问运算符“->”函数重载的一般形式为:

数据类型		类名::operator->();

例:

class PClass
{
	int n; double m;
public:
	PClass *operator->()
	{
		return this;
	}
	void setvalue(int n1, double m1)
	{
		n = n1; m = m1;
	}
	void disp()
	{
		cout << "n=" << n << ",m=" << m<<endl;
	}
};

void main()
{
	PClass s;
	s->setvalue(10, 20.5);
	s->disp();
	s.setvalue(20, 89.8);
	s.disp();
	system("pause");
}

结果:
在这里插入图片描述
注:上述程序中,重载“->”运算符的成员函数,该函数返回当前对象的指针。从而导致“s->disp();”和“s.disp();”两个语句都是正确的,实际上,前者通过调用重载“->”运算符成员函数转换成后者的格式。

重载双目运算符

重载双目运算符为成员函数

假设有一个类A,对于双目运算符op,如果重载运算符op使之能够实现表达式“obj1 op obj2”,其中obj1和obj2均为A类的对象。
若把op重载为A类的成员函数,该函数只有一个形参,形参的类型是obj2所属的类型。经过重载之后,表达式“obj1 op obj2”解释为:

obj1.operator	op(obj2)

左边的Obj1通过this指针传递,右边的对象obj2由参数传递。

重载双目运算符为友元函数

假设有一个类A,对于双目运算符op,如果重载运算符op使之能够实现表达式“obj1 op obj2”,其中obj1和obj2均为A类的对象。
若把op重载为A类的友元函数,该函数有两个形参,经过重载之后,表达式“obj1 op obj2”解释为:

obj1	op(obj1,obj2)

左右两个对象obj1,obj2都由参数传递。
下面是重载运算符为成员函数和重载为友元函数的对比:
例:
重载运算符为成员函数:

#include<iostream>
using namespace std;
class vector
{
	int x, y;
public:
	vector(){ };//默认构造函数
	vector(int x1, int y1)//重载函数
	{
		x = x1;y=y1;
	}
	vector operator+(vector v)
	{
		vector tmp;
		tmp.x = x + v.x;
		tmp.y = y + v.y;
		return tmp;
	}
	vector operator-(vector v)
	{
		vector tmp;
		tmp.x = x - v.x;
		tmp.y = y - v.y;
		return tmp;
	}
	void display()
	{
		cout << "(" << x << "," << y << ")";
	}
};
void main()
{
	vector v1(2, 3), v2(3, 5), v3, v4;
	cout << "v1:"; v1.display();
	cout << "v2:"; v2.display();
	v3 = v1 + v2; v4 = v1 - v2;
	cout << "v3:"; v3.display();
	cout << "v4:"; v4.display();
	system("pause");
}

重载运算符为友元函数:

#include<iostream>
using namespace std;
class vector
{
	int x, y;
public:
	vector(){ };//默认构造函数
	vector(int x1, int y1)//重载函数
	{
		x = x1; y = y1;
	}
	friend vector operator+(vector v, vector v1)
	{
		vector tmp;
		tmp.x = v1.x + v.x;
		tmp.y = v1.y + v.y;
		return tmp;
	}
	friend vector operator-(vector v, vector v1)
	{
		vector tmp;
		tmp.x = v1.x - v.x;
		tmp.y = v1.y - v.y;
		return tmp;
	}
	void display()
	{
		cout << "(" << x << "," << y << ")";
	}
};
void main()
{
	vector v1(2, 3), v2(3, 5), v3, v4;
	cout << "v1:"; v1.display();
	cout << "v2:"; v2.display();
	v3 = v1 + v2; v4 = v1 - v2;
	cout << "v3:"; v3.display();
	cout << "v4:"; v4.display();
	system("pause");
}

注意:重载运算符为成员函数和友元函数时关键的区别在于成员函数具有this指针,而友元函数没有this指针。

重载比较运算符

比较运算符函数重载必须返回true(非0)和false(0)。

重载赋值运算符

重载“+=”和“-=”运算符

程序中重载运算符“+=”和“-=”与标准数据类型“+=”和“-=”不完全相同。调用重载的运算符时,例如V1+=V2,并不改变V1的值,而后者会改变运算符左边的值。

#include<iostream>
using namespace std;
class vector
{
	int x, y;
public:
	vector(){ }
	vector(int x1, int y1)
	{
		x = x1; y = y1;
	}
	friend vector operator+=(vector v1, vector v2)//友元函数方式实现
	{
		vector tmp;
		tmp.x = v1.x + v2.x;
		tmp.y = v1.y + v2.y;
		return tmp;
	}
	vector operator-=(vector v)//成员函数方式实现
	{
		vector tmp;
		tmp.x = x - v.x;
		tmp.y = y - v.y;
		return tmp;
	}
	void display(){ cout << "(" << x << "," << y << ")"<<endl; }
};

void main()
{
	vector v1(3, 2), v2(6, 5), v3, v4;
	v1.display() ;
	v2.display();
	v3 = v1 += v2;
	v4 = v2 -= v1;
	v3.display();
	v4.display();
	v1.display();
	v2.display();
	system("pause");
}

在这里插入图片描述
可以看出,重载+=与-=运算符中,v1+v2的运算不改变v1的值。

重载=运算符

赋值运算符=的原有含义是将赋值号右边表达式的结果复制给赋值号左边的变量,通过运算符=的重载将赋值号右边的数据成员函数依次复制给左边对象的数据成员中。

重载下标运算符

下标运算符“[ ]”通常用于获取数组的某个元素,重载下标运算符可以实现数组下标的越界检测等。下标运算符重载只能作为类的成员函数,不能作为类的友元函数。

//设置一个Assoc类,其中用一个数组表示每个单词的情况,而且每个单词除了他出现的次数,
//还应保存该单词本身,因此定义一个结构。该类中有一个重载运算符“[ ]”成员函数,用来返回
//某个单词已经出现的次数,返回值是一个引用,可用于改变值。在每查找到一种单词后返回以出
//现的次数在运算符后边进行++运算,相对于返回值++,间接地起到每找到一个单词便将它的出现次数
//+1的目的。
#include<iostream>
#include<string.h>
using namespace std;
struct Pair//说明结构体类型
{
	char *name;//单词
	int num;//出现的次数
};
class Assoc
{
	struct Pair *vec;//指向结构体变量的指针
	int size;//分配总的单元个数
	int used;//已使用的单元个数
public:
	Assoc(int m)//构造函数
	{
		size = (m > 16) ? m : 16;//size至少大于16
		used = 0;
		vec = new Pair[size];//分配空间
	}
	int & operator [] (char *);
	void disp();
};
int & Assoc::operator[](char *p)//返回的是pp->num的引用
{
	struct Pair *pp;
	for (pp = vec; pp < vec + used; pp++)//在已有的单词中查找
	if (strcmp(p, pp->name) == 0)//若找到返回次数
		return pp->num;
	pp = &vec[used++];//在已有的单词中未找到,则使用的单元个数+1
	pp->name = new char[strlen(p) + 1];//分配一个单元空间
	strcpy(pp->name, p);
	pp->num = 0;
	return pp->num;//返回0
}
void Assoc::disp()
{
	cout << "单词出现次数统计:" << endl;
	for (int i = 0; i < used; i++)
	{
		cout << " " << vec[i].name << ":" << vec[i].num << "次" << endl;
	}
}
void main()
{
	char buf[16];
	Assoc vecc(20);//设置20个单元存放单词
	int k = 10;
	cout << "请输入" << k << "个单词" << endl;
	for (int i = 0; i < k; i++)
	{
		cout << "第" << i + 1 << "个单词:";
		cin >> buf;
		vecc[buf]++;//调用“[]”,并将该单词出现次数增1
	}
	vecc.disp();
	system("pause");
}

在这里插入图片描述

重载new和delete运算符

new和delete只能被重载为类的成员函数,不能重载为友元。而且,无论是否使用关键字static进行修饰,重载了的new和delete均为类的静态成员函数。
运算符new重载的一般格式如下:

void	*类名::operator	new(size_t,参数表);

在带有“参数表”时,应注意使用重载new的方式。例如,若有一个类X有如下重载new的成员函数:

void* operator new(size_t size,int x,int y,int z)
{……
}

则使用重载new的方式如下:

x*pX=new(1,2,3) X;

运算符delete重载的格式一般如下:

void *类名“::operator delete(void*,参数表);

重载类型转换运算符

重载类型转换运算符格式:

operator 类型名()
{
	函数体;
}

类型转换运算符没有返回类型,因为类型名就代表了它的返回类型,而且没有任何参数。
转换运算符重载的缺点是无法定义其类对象运算符操作的真正含义,因为只能进行相应的对象成员数据和一般数据变量的转换操作。

重载函数调用运算符

函数调用运算符“()”只能说明成类的非静态成员函数,该函数具有以下的一般格式:

函数类型 类名::operator()(参数表)

与普通函数一样,重载了的函数调用运算符可以事先带有零个或多个参数,但不得带缺省的参数。

#include<iostream>
using namespace std;
class PClass
{
public:
	double operator()(double x, double y) const;
	double operator()(double x) const;
};

double PClass::operator()(double x, double y)const
{
	if (x < y) return (y);
	else return (x);
}
double PClass::operator()(double x) const
{
	if (x < 0) return (-x);
	else return (x);
}
void main()
{
	PClass fun;
	cout << fun(1.2, 2.3) << endl;
	cout << fun(-6) << endl;
	system("pause");
}

在这里插入图片描述

  • 34
    点赞
  • 139
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Qt智能指针--QWeakPointer ⽂章⽬录 概述 前⾯我们说到的是⼀个强引⽤类型的智能指针,⽽QWeakPointer是⼀个弱引⽤类型的智能指针,和C++中的weak_ptr功能是⼀样的。 QWeakPointer 是为配合 QSharedPointer ⽽引⼊的⼀种智能指针,它更像是 QSharedPointer 的⼀个助⼿(因为它不具有普通指针的⾏ 为,没有重载operator*和->)。它的最⼤作⽤在于协助 QSharedPointer ⼯作,像⼀个旁观者⼀样来观测资源的使⽤情况。 QWeakPointer QWeakPointer不能⽤于直接取消引⽤指针,但它可⽤于验证指针是否已在另⼀个上下⽂中被删除。并且QWeakPointer对象只能通过 QSharedPointer的赋值来创建。 需要注意的是,QWeakPointer不提供⾃动转换操作符来防⽌错误发⽣。即使QWeakPointer跟踪指针,也不应将其视为指针本⾝,因为它 不能保证指向的对象保持有效。 说了那么多,QWeakPointer到底有什么⽤呢? 答案就是:解除循环引⽤。 在概述中我们说到,QWeakPointer 是为配合 QSharedPointer ⽽引⼊的⼀种智能指针。⽽什么叫循环引⽤,就是说:两个对象互相使⽤ ⼀个 QSharedPointer成员变量指向对⽅(你中有我,我中有你)。由于QSharedPointer是⼀个强引⽤的计数型指针,只有当引⽤数为0 时,就会⾃动删除指针释放内存,但是如果循环引⽤,就会导致QSharedPointer指针的引⽤永远都不能为0,这时候就会导致内存⽆法释 放。 所以QWeakPointer诞⽣了,它就是为了打破这种循环的。并且,在需要的时候变成QSharedPointer,在其他时候不⼲扰 QSharedPointer的引⽤计数。它没有重载 * 和 -> 运算符,因此不可以直接通过 QWeakPointer 访问对象,典型的⽤法是通过 lock() 成 员函数来获得 QSharedPointer,进⽽使⽤对象。 ⽰例 ⾸先,我们来看⼀个QSharedPointer循环使⽤的⽰例: #include <QWidget> #include <QDebug> #include <QWeakPointer> #include <QSharedPointer> class Children; class Parent { public: ~Parent(){ qDebug() << __FUNCTION__; } QSharedPointer<Children> m_pChildren; }; class Children { public: ~Children(){ qDebug() << __FUNCTION__; } QSharedPointer<Parent> m_pParent; }; class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = 0); ~Widget(); void test(); }; #include "widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { test(); } Widget::~Widget() { qDebug() << __FUNCTION__; } void Widget::test() { QSharedPointer<Parent> parent(new Parent()); QSharedPointer<Children> children(new Children()); if(parent && children){ parent->m_pChildren = children; children->m_pParent = parent; } } 在构造函数中调⽤test()函数,执⾏过后应该会⾃动释放parent和children对象,但是由于相互引⽤,在退出之前,引⽤计数为2,退出 之后引⽤计数还是1,所以导致不能⾃动释放,并且此时这两个对象再也⽆法访问到。运⾏过后发现并没有进⼊到Parent和Children的析构 函数中。这就导致了内存泄漏。 那么该如何解决这个问题呢? 很简单,直接将上述代码中的 QSharedPointer<Children> m_pChildren; QSharedPointer<Parent> m_pParent; 改成 QWeakPointer<Children> m_pChildren; QWeakPointer<Parent> m_pParent; 这时候
C++智能指针shared_ptr讲解与使⽤ ⼿动管理的弊端 在简单的程序中,我们不⼤可能忘记释放 new 出来的指针,但是随着程序规模的增⼤,我们忘了 delete 的概率也随之增⼤。在 C++ 中 new 出来的指针,赋值意味着引⽤的传递,当赋值运算符同时展现出"值拷贝"和"引⽤传递"两种截然不同的语义时,就很容易导致"内 存泄漏"。 ⼿动管理内存带来的更严重的问题是,内存究竟要由谁来分配和释放呢?指针的赋值将同⼀对象的引⽤散播到程序各处,但是该对象的释放 却只能发⽣⼀次。当在代码中⽤了⼀个资源指针,该不该释放 delete 掉它?这个资源极有可能同时被多个对象拥有着,⽽这些对象中的 任何⼀个都有可能在之后使⽤该资源,其余指向这个对象的指针就变成了"野指针";那如果不 delete 呢?也许你就是这个资源指针的唯 ⼀使⽤者,如果你⽤不 delete,内存就泄漏了。 资源的拥有者是系统,当我们需要时便向系统申请资源,当我们不需要时就让系统⾃⼰收回去(Garbage Collection)。当我们⾃⼰处理的时 候,就容易出现各种各样的问题。 C++中的智能指针 ⾃C++11起,C++标准提供两⼤类型的智能指针: 1. Class shared_ptr实现共享式拥有(shared ownership)概念。多个智能指针可以指向相同对象,该对象和其相关资源会在"最后⼀个引 ⽤(reference)被销毁"时候释放。为了在结构复杂的情境中执⾏上述⼯作,标准库提供了weak_ptr、bad_weak_ptr和 enable_shared_from_this等辅助类。 2. Class unique_ptr实现独占式拥有(exclusive ownership)或严格拥有(strict ownership)概念,保证同⼀时间内只有⼀个智能指针 可以指向该对象。它对于避免资源泄露(resourece leak)——例如"以new创建对象后因为发⽣异常⽽忘记调⽤delete"——特别有⽤。 注:C++98中的Class auto_ptr在C++11中已不再建议使⽤。 share_ptr 智能指针是(⼏乎总是)模板类,shared_ptr 同样是模板类,所以在创建 shared_ptr 时需要指定其指向的类型。shared_ptr 负责在不使 ⽤实例时释放由它管理的对象,同时它可以⾃由的共享它指向的对象。 shared_ptr 使⽤经典的 "引⽤计数" 的⽅法来管理对象资源。引⽤计数指的是,所有管理同⼀个裸指针( raw pointer )的 shared_ptr,都共享⼀个引⽤计数器,每当⼀个 shared_ptr 被赋值(或拷贝构造)给其它 shared_ptr 时,这个共享的引⽤计数器就加 1,当⼀个 shared_ptr 析构或者被⽤于管理其它裸指针时,这个引⽤计数器就减1,如果此时发现引⽤计数器为0,那么说明它是管理这个 指针的最后⼀个 shared_ptr 了,于是我们释放指针指向的资源。 在底层实现中,这个引⽤计数器保存在某个内部类型⾥(这个类型中还包含了 deleter,它控制了指针的释放策略,默认情况下就是普通的 delete操作),⽽这个内部类型对象在 shared_ptr 第⼀次构造时以指针的形式保存在 shared_ptr 中(所以⼀个智能指针的析构会影响到 其他指向同⼀位置的智能指针)。shared_ptr 重载了赋值运算符,在赋值和拷贝构造另⼀个 shared_ptr 时,这个指针被另⼀个 shared_ptr 共享。在引⽤计数归零时,这个内部类型指针与 shared_ptr 管理的资源⼀起被释放。此外,为了保证线程安全性,引⽤计数 器的加1,减1操作都是 原⼦操作,它保证 shared_ptr 由多个线程共享时不会爆掉。 对于 shared_ptr 在拷贝和赋值时的⾏为,《C++Primer第五版》中有详细的描述: 每个 shared_ptr 都有⼀个关联的计数值,通常称为引⽤计数。⽆论何时我们拷贝⼀个 shared_ptr,计数器都会递增。 例如,当⽤⼀个 shared_ptr 初始化另⼀个 shred_ptr,或将它当做参数传递给⼀个函数以及作为函数的返回值时,它所关联的计数 器就会递增。当我们给 shared_ptr 赋予⼀个新值或是 shared_ptr 被销毁(例如⼀个局部的 shared_ptr 离开其作⽤域)时,计数 器就会递减。⼀旦⼀个 shared_ptr 的计数器变为0,它就会⾃动释放⾃⼰所管理的对象。 下⾯看⼀个常见⽤法,包括: 1. 创建 shared_ptr 实例 2. 访问所指对象 3. 拷贝和赋值操作 4. 检查引⽤计数。 #include <iostream> #include

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值