c++重载运算符

一、重载输出运算符<<

1、通常情况下,输出运算符的第一个形参是非常量的ostream对象的引用,是非常量是因为:向流写入会改变其状态,是引用是因为:我们无法直接复制一个ostream对象

第二个形参一般来说是一个常量的引用,该常量是我们想要打印的类型

2、为了与其他输出运算符保持一致,operator<<一般要返回它的ostream形参(的引用)

class Node
{
private:
	int num;
	string name;
public:
	friend ostream& operator<<(ostream& out, const Node& a);
}; 
ostream& operator<<(ostream& out, const Node& a)
{
	out << a.num << ' ' << a.name;
	return out;
}

3、为了和内置类型的输出运算符保持一致,重载输出运算符时应该尽量减少格式化操作,尤其是换行

4、与iostream标准库兼容的输入输出运算符必须是普通的非成员函数,不能是类的成员函数,否则他们的左侧运算对象将是我们类的一个对象,此时我们一般将IO运算符声明为友元

二、重载输入运算符>>

1、通常情况下,输出运算符的第一个形参是非常量的istream对象的引用,第二个形参一般来说是一个非常量对象的引用,第二个形参之所以是非常量是因为要向该对象写入数据

2、输入时可能发生错误:下一个数据类型不匹配;读入到文件末尾,因此我们需要对是否输入正确进行检查,如果发生错误,应该将对象恢复为默认状态

class Node
{
private:
	int num;
	string name;
public:
	friend istream& operator>>(istream& in, Node& a);
};
istream& operator>>(istream& in, Node& a)
{
	string tname;
	in >> a.num >> tname;
	if (in)
		a.name = tname;
	else
		a = Node();
	return in;
}

三、算术运算符

1、我们通常把算术和关系运算符定义成非成员运算符以允许对左侧右侧的运算对象进行转换

这些运算符一般不需要改变运算对象的状态,故形参都是常量的引用

2、算数运算符通常会计算它的两个运算对象并得到一个新值,这个值有别于任意一个运算对象,常常位于一个局部变量之内,操作完成后返回该局部变量的副本作为其结果

3、类如果定义了算术运算符,那么往往会定义对应的复合赋值运算符,此时最有效的方式是结合复合赋值运算符来定义或实现算术运算符

Node operator+(const Node& a, const Node& b)
{
	Node c(a);
	c.num += b.num;
	c.name += c.name;
	return c;
}
Node operator+(const Node& a, const Node& b)
{
	Node c(a);
    c += b;
	return c;
}

四、关系运算符

1、相等运算符

bool operator==(const Node& a, const Node& b)
{
	return a.name == b.name && a.num == b.num;
}
bool operator!=(const Node& a, const Node& b)
{
	return !(a == b);
}

2、关系运算符

由于关联容器和一些算法要用到小于运算符,所以我们往往重载operator<会比较有用

friend bool operator<(const Node& a, const Node& b)
{
	if (a.num != b.num)
		return a.num < b.num;
	else
		return a.name < b.name;
}

五、赋值运算符

1、除了拷贝赋值和移动赋值运算符,还可以将花括号列表作为参数(vector可以用花括号列表初始化)

Node& operator=(initializer_listil)
{
	auto date = alloc_n_copy(il.begin(), il.end());	//分配内存空间并拷贝元素(自定义)
	free();
	elements = date.first;
	first_free = cap = date.second;
	return *this;
}

2、复合赋值运算符

为了和内置类型的复合赋值运算符保持一致,类内的复合赋值运算符要返回左侧的运算对象的引用

Node& operator+=(const Node& a)
{
	num += a.num;
	name += a.name;
	return *this;
}

六、下标运算符

1、表示容器的类通常通过元素在容器中的位置访问元素,这些类一般会定义下标运算符

2、下标运算符通常以所访问元素的引用作为返回值,这样做的好处是下标可以出现在赋值运算符的任意一端(=a[]或a[]=)

class Node
{
public:
	string* sp;
public:
	string& operator[](size_t n)
	{
		return sp[n];
	}
	//......
};

七、递增运算符和递减运算符

1、前置递增、递减运算符

class Node
{
private:
	int num;
public:
	Node& operator++()
	{
		num++;
		return *this;
	}
};

2、后置递增、递减运算符

后置的运算符要返回运算之前的值,后置版本会接受一个额外的int形参(值为0),它唯一的作用就是区分前置和后置

class Node
{
private:
	int num;
public:
	Node operator++(int)
	{
		Node old = *this;
		++*this;		//调用前置版本
		return old;
	}
};

3、显示调用

a.operator++();        //前置版本
a.operator++(0);       //后置版本

八、成员访问运算符

string& operator*()const
{
	return (*p)[cur];
}
string* operator->()const
{
	return &this->operator*();
}

九、函数调用运算符

重载了函数调用运算符,我们可以像使用函数一样使用类的对象,因为这样的类同时也能存储状态,比普通函数更加灵活

如果类定义了调用运算符,则该类的对象称为函数对象

class Node
{
private:
	int* array;
public:
	int getarr(int k)const
	{
		return array[k];
	}
};

你可能感兴趣的:(C++,c++,开发语言)