C++primer读书笔记8重载操作符

函数的重载操作符中也有不少细节

1 重载操作符特点

重载操作符本质上就是重载函数,它的特点:

<1>除了函数调用操作符‘()’外,所有的重载操作符的形参数目必须与操作符的操作数目相等。

<2>重载操作符的操作数必须至少有一个类类型,否则就不是重载而是原型了。

<3>优先级和结合性不会发生改变,例如对于算数操作符的重载,*的优先级肯定还是大于+

<4>不具有短路求值特性(就是根据已有的值就可以判断出最终的逻辑结果的话就会停止计算)

<5>操作符可以可以是类的成员函数,或者普通的非成员函数。

区别是当是类的成员函数,那么this形参为第一个操作数

<6>重载操作符的使用,既可以像操作符之前的调用方式那样进行操作,也可以像函数调用那样。

Item item1 ,item2;
item1 + item2;		// call method 1
operator+(item1,item2)	// call method 2
// if + is operator of class
item1 + item2;
item1.operator+(item2);

2 重载操作符的谨记

<1> 赋值操作符,取地址操作符,逗号操作符

这些操作符对类类型都有默认的含义,也就是如果没有自己重载,编译器就会自己去合成这些这些操作符函数。

这些操作符都是具有自己的意义的,如果我们自己重写就会失去原本的意义。

<2> 赋值操作‘=’,下标‘[]‘,箭头‘->’,调用操作度‘()’必须定义为类成员,定义为类成员操作符,否则会出现变异错误。

<3>复合赋值操作符‘+=’等,一斤改变类状态的操作符自增,自减等一般也要定义为类成员

<4>对称的操作符如,算数运算符 ‘+’,‘-’,‘*’,‘%’一般最好定义为非成员操作符

3 类成员操作符

类成员操作符比非成员操作符要看起来要少一个参数,其实这个少的参数是this参数,而且他被限定为操作符的第一个操作数。

4 输入输出操作符的重载

<1>输出操作符中应该尽量包含少的格式化,因为这样可能会违背使用者要表达的意愿

<2>IO操作符必须为非成员函数

这个说一定是为了兼容对象的输出以及一般类型变量的输出。但是如果把输出操作符设置为类成员函数并没有编

译错误,并且实际上也是可行的。如下所示:

// output.h
#pragma once
#include <iostream>
using namespace std;
class Input
{
public:
Input(void);
~Input(void);
ostream& operator <<(ostream& out);
private:
string str;
int fun;
};

// output.cpp
#include "Input.h"
Input::Input(void)
{
str = "keep move on";
fun = 1001;
}
Input::~Input(void)
{
}
ostream& Input::operator<<(ostream& out)
{
out<<"persist on"<<fun<<endl;
return out;
}

//main.cpp
#include "Input.h"
void main()
{
Input in;
in<<cout<<"I'll always find you"<<endl;
}

输出结果:

persist on1001

I‘ll always find you

结论:这样因为对象必须作为第一个参数,如:in<<cout<<"I‘ll always find you"<<endl;这样和平时的书写习惯不一致,所以最好不好把输出输入操作符定义为类成员操作符。

5 算数操作符的重载

算数操作符要谨记的是它返回一个新的值,不是引用,这个值是两个值计算的结果。因为如果返回的是临时变量的引用的话,临时变量会被销毁。

6 相等操作符

通常比较每个类的数据成员,如果所有的对应成员都相等,那么就认为两个对象相等。

7 关系操作符

关系操作符通常用于需要对类对象进行排序的时候,所以它和相等操作符有点不一样,例如小于操作符只需要类对象数据成员中的某一个而不是所有的,容器中的对象的相等的定义也是根据‘<’来定义的,如果 A !<B && B

!<A ,那么A和B就相等。

8 赋值操作符

<1>一个类可以定义多个赋值操作符

<2>没有自己定义赋值操作符,编译器会自己定义一个

<3>赋值操作符必须返回对*this的引用,这样就不需要重新创建副本,销毁之前的版本一系列复杂的操作

9 下标操作符

C++中的容器在检索单个元素的时候一般会定义下标操作符‘[]’,因为下标操作符是跟类对象密切相关的,所以它必须定义为类的成员。定义类的下标操作符的时候,一般要定义连个版本,一个是const成员并返回const引用,一个是非const成员并且

返回引用

#pragma once
#include <vector>
using namespace std;
class XB
{
public:
XB(void);
~XB(void);
int& operator [](const size_t index);
const int& operator [](const size_t index)const;
private:
vector<int> data;
};

//XB.cpp
#include "XB.h"
XB::XB(void)
{
data = vector<int>(5,2);
}

XB::~XB(void)
{
}

int& XB::operator [](const size_t index)
{
return data[index];
}

const int& XB::operator[](const size_t index)const
{
return data[index];
}

//main.cpp
#include "XB.h"
#include <iostream>
using namespace std;
void main()
{
XB xb;
cout<<xb[3];
xb[1] = 10;
cout<<xb[1];
system("pause");
}

10 解引用操作符‘*‘和箭头操作符‘->‘

这两个操作符经常用在智能指针上面,就是把一些类对象给它赋予指针的功能。智能指针除了要操作的类外,另外包含两个类,一个类用来存放类对象的指针,以及使用计数。另一个类用来操作使用计数,并根据使用计数的大小来确定是否释放对象。下面的例子中Tv类是要操作类,TvPtr是包含使用技术和操作类的类。

#include <iostream>
using namespace std;
class Tv
{
public:
Tv(void):m_height(100),m_width(390){};
~Tv(void){};
int func(){return m_height * m_width;}
private:
int m_height;
int m_width;
};

class TvPtr
{
friend class TvPtrCnt;
Tv* sp;
size_t use;
TvPtr(Tv* p):sp(p),use(1){}
~TvPtr(){delete sp;}
};

class TvPtrCnt
{
public:
TvPtrCnt(Tv* p):ptr(new TvPtr(p)){}
TvPtrCnt(const TvPtrCnt& orig):ptr(orig.ptr){++ptr->use;};
TvPtrCnt& operator =(const TvPtrCnt &orig){++ptr->use;}
~TvPtrCnt(){if(--ptr->use ==0) delete ptr;}

Tv& operator*(){return *ptr->sp;}
Tv* operator->(){return ptr->sp;}
const Tv& operator*()const{return *ptr->sp;}
const Tv* operator->()const{return ptr->sp;}
private:
TvPtr* ptr;
};
void main()
{
Tv *p = new Tv();
TvPtrCnt zp(p);
std::cout<<(*zp).func()<<std::endl;
std::cout<<zp->func()<<std::endl;

system("pause");
}

注意:这里说的是箭头操作符‘->’它不同于一般的操作符,就那point->action来讲:

<1>如果point是指针,指向具有action成员的对象,那么编译器将代码编译为调用该对象的action的成员

<2>如果action是定义了‘->’操作符的类的一个成员,那么point->action与point.operation->()->action等同

<3>否则,代码错误

总结:使用箭头‘->’操作符,必须返回一个指针类型或者自定义了‘->’的类对象。

解引用操作符和箭头操作符赋予了类对象看似指针的行为功能。

11 自增操作符,自减操作符

这些操作符又分为前自增(prefix),后自增(postfix),前自减(prefix),后自减(postfix)

<1>prefix

XXX& operator++();
XXX& operator--();

<2>postfix

为了区分前和后的区别,在后自增,后自减的函数中增加了一个int的参数,编译器提供0做位实参值

XXX operator++(int);
XXX operator--(int);

注意:后自增,后自减的返回值不是引用,因为返回的是一个临时的变量

<3> 显式的调用前缀后缀操作符

XXX.operator++(); //prefix
XXX.operator++(0); //postfix

12 调用操作符和函数对象

<1>

类重载函数调用操作符‘()‘,这样通过控制通过在对象后面加括弧,加参数,可以把类对象当做函数一样使用,我们把定义了调用操作符的类,器对象成为函数对象。

#include <iostream>
using namespace std;
class Abs
{
public:
Abs(){}
~Abs(){}
int operator ()(int val){return val <0 ? -val : val;}
};
void main()
{
Abs XX;
cout<<XX(-108)<<endl;
system("pause");
}

<2>函数对象用于标准库算法

标准库中有很多函数,需要提供函数指针或者函数对象作为参数,例如count_if()函数

<3>标准卡中还有很多定义的函数对象

plus<Type>,minus<Type>,multiplies<Type>,greater<Type>...

很多函数对象都是存在<functional>头文件中的函数对象

plus<int> Add;

cout<<Add(100,-23)<<endl;

时间: 2024-11-10 14:33:43

C++primer读书笔记8重载操作符的相关文章

C++ Primer 学习笔记_64_重载操作符与转换 --转换与类类型【下】

重载操作符与转换 --转换与类类型[下] 四.重载确定和类的实参 在需要转换函数的实参时,编译器自动应用类的转换操作符或构造函数.因此,应该在函数确定期间考虑类转换操作符.函数重载确定由三步组成: 1)确定候选函数集合:这些是与被调用函数同名的函数. 2)选择可行的函数:这些是形参数目和类型与函数调用中的实参相匹配的候选函数.选择可行函数时,如果有转换操作,编译器还要确定需要哪个转换操作来匹配每个形参. 3)选择最佳匹配的函数.为了确定最佳匹配,对将实参转换为对应形参所需的类型转换进行分类.对于

C++ Primer 学习笔记_58_重载操作符与转换 --重载操作符的定义

重载操作符与转换 --重载操作符的定义 引言: 明智地使用操作符重载可以使类类型的使用像内置类型一样直观! 重载操作符的定义 重载操作符是具有特殊名称的函数:保留字operator后接定义的操作符符号.如: Sales_item operator+(const Sales_item &,const Sales_item &); 除了函数调用操作符之外,重载操作符的形参数目(包括成员函数的隐式this指针)与操作符的操作数数目相同.函数调用操作符可以接受任意数目的操作数. 1.重载的操作符名

C++ Primer 学习笔记_59_重载操作符与转换 --输入/输出、算术/关系操作符

重载操作符与转换 --输入/输出.算术/关系操作符 支持I/O操作的类所提供的I/O操作接口,一般应该与标准库iostream为内置类型定义的接口相同,因此,许多类都需要重载输入和输出操作符. 一.输出操作符<<的重载 为了与IO标准库一致,操作符应接受ostream&作为第一个形参,对类类型const对象的引用作为第二个形参,并返回ostream形参的引用! ostream &operator<<(ostream &os,const ClassType &

C++ Primer 学习笔记_63_重载操作符与转换 --转换与类类型【上】

重载操作符与转换 --转换与类类型[上] 引言: 在前面我们提到过:可以用一个实参调用的非explicit构造函数定义一个隐式转换.当提供了实参类型的对象需要一个类类型的对象时,编译器将使用该转换.于是:这种构造函数定义了到类类型的转换. 除了定义到类类型的转换之外,还可以定义从类类型到其他类型的转换.即:我们可以定义转换操作符,给定类类型的对象,该操作符将产生其他类型的对象.和其他转换一样,编译器将自动应用这个转换. 一.转换为什么有用? 定义一个SmallInt的类,该类实现安全小整数,这个

C++ Primer 学习笔记_60_重载操作符与转换 --赋值、下标、成员訪问操作符

重载操作符与转换 --赋值.下标.成员訪问操作符 一.赋值操作符 类赋值操作符接受类类型形參,通常该形參是对类类型的const引用,但也能够是类类型或对类类型的非const引用.假设未定义这个操作符,则编译器将合成它.类赋值操作符必须是类的成员,以便编译器能够知道是否须要合成一个.并且还能够为一个类定义很多附加的赋值操作符,这些赋值操作符会由于右操作数的不同而构成重载!如string类型: string car("Volks"); car = "Studebaker"

C++ Primer 学习笔记_61_重载操作符与转换 --自增/自减操作符

重载操作符与转换 --自增/自减操作符 引言: 自增,自减操作符常常由诸如迭代器这种类实现,这种类提供相似于指针的行为来訪问序列中的元素.比如,能够定义一个类,该类指向一个数组并为该数组中的元素提供訪问检查: class CheckedPtr { public: //这个类没有默认构造函数,必须提供指向数组的指针. /**构造函数的參数是两个指针:一个指向数组的開始,还有一个指向数组的末端. *构造函数用这两个指针初始化 beg 和 end *并将 curr 初始化为指向第一个元素 */ Che

C++ Primer 学习笔记_62_重载操作符与转换 --调用操作符和函数对象

重载操作符与转换 --调用操作符和函数对象 引言: 能够为类类型的对象重载函数调用操作符:一般为表示操作的类重载调用操作符! struct absInt { int operator() (int val) { return val > 0 ? val : -val; } }; 通过为类类型的对象提供一个实參表而使用调用操作符,所用的方式看起来系那个一个函数调用: absInt absObj; int i = -1; cout << absObj(i) << endl; 虽然

C++ primer读书笔记10-继承

封装,继承,多态是C++的三大基本概念,这里着重总结一下继承相关的东西 1 类派生列表 类派生列表指定派生类要继承的基类,派生列表中有一个或者多个基类如: class B : public A1,protected A2,private A3 但是单继承时最常见的,多继承不多见 2 派生类的定义 派生类继承时,会包含父类的所有成员,即便私有成员不能被访问.父类中的虚函数,在派生类中一般也要定义,如 果不定义的话,派生类将继承基类的虚函数 3 基类必须是已经定义的 一个仅仅声明的类,是不能出现在派

C++primer读书笔记11-多态

多态也是C++中的一个重要的方面,多态和动态类型,虚函数本质上是指相同的事情. 1 虚函数 类中的成员函数原型前面加上virtual 表面这个函数是个虚函数.虚函数的目的是为了在继承它的派生类中重新定义这个函数,以便于通过基类的指针或引用在运行时对派生类的函数进行调用. 2 派生类和虚函数 派生类一般情况下要重定义所继承的虚函数,有几个注意事项. <1>虚函数的声明必须和基类中的函数声明原型完全一致,例外的是当基类返回基类型的指针或者引用的时候,派生类可以派生类类型的指针或者引用 <2&