十四章 札记--C++ primer 之旅

重载运算符:具有特殊名称的函数,operator + 操作符符号 ,具有返回类型和形参表。
注意: 重载操作符必须具有至少一个类类型枚举类型的操作数。
大多数重载操作符可以定义为普通非成员函数或类的成员函数
作为类成员的重载函数,形参比操作数数目少1,隐含的this 形参,限定为第一个操作数
规则:一般将算术和关系操作符定义为非成员函数,而将赋值操作符定义为成员
操作符重载和友元关系:
     操作符定义为非成员函数时,通常必须将它们设置为所操作类的友元
重载输出输入符: 右操作数是输入输出流,所以应该只能定义成非成员函数。返回输入输出流引用。
重载操作符的设计
注意:不要重载具有内置含义的操作符: 逗号、取地址、逻辑与、逻辑或等具有有用的内置含义。如果重载了就不能再使用这些内置含义了
相等测试操作应使用 operator==
测试对象是否为空的操作可用逻辑非操作符 operator!
相等和关系操作符: 将要用作关联容器键类型的类应定义< 操作符
存储于顺序容器中,类通常应该定义 == 操作符和 <  操作符, 这样sort < 和 find == 算法就可以用
输入输出操作符:
      输出操作符<< 的重载:操作符应接受ostream& 作为第一个形参(引用:因为无法复制输出流),对类类型const对象的引用作为第二个形参,并返回对 ostream形参的引用。
注意: 第二个形参一般应是对要输出的类类型的引用,引用避免复制实参。可以是const, 因为输出一个对象不应该改变该对象。使形参为const引用,就可以使用同一个定义来输出const 对象和非const 对象。
再次再次谨记 delete 和 new 的含义: new  操作时动态申请内存,然后返回内存的地址,返回的值可以指针; 而delete 就是 释放内存空间,所以传给delete 的操作数应该是内存的地址值,所以只能是指针或对象和引用的取地址。
今天在想可以删除引用对象所引用的内存位置吗?
  google 了一下,还真的有人做过了?
code:
      int &p = * new int(4);         //   注意,引用的初始化必须是对象,所以对new来的指针值解引用
            delete &p ;                        //    传给delete 内存地址值
输入操作符 >> 的重载: 
     第一个形参和输出操作符一样,输入流的引用;第二个形参是对要读入的对象的非const 应用。必须是非const ,应为输入肯定是要改变对象的。
输入期间的错误:(重载输入操作符注意要测试流!!!)
     可能发生的错误: 1) 可能因为提供的值不正确而失败。类型不匹配
                                  2)任何读入都可能碰到输入流中的文件结束或其他错误
     因此,必须测试流如果发生了错误,还必须考虑错误恢复,是对象处于可用的状态。
    
处理错误: 发生输入错误时,输入操作符可以通过设置形参的条件状态进行信息反馈。通常仅需设置failbit
算术操作符 和 关系操作符:一般定义为非成员函数。
    加法操作符并不改变操作数的状态,操作数是对const 对象的引用。返回一个新的类类型对象。 为了与内置操作符保持一致,加法返回一个右值。而非引用
相等操作符:
  • 如果类定义了operator == , 则应该也定义了operator!= 。
  • 相等和不相等 应该相互联系定义, 让其中一个操作符完成比较对象的实际工作,而另外一个则调用前者。
类赋值操作符必须是类的成员函数,以便编译器可以知道是否需要合成一个
赋值操作符可以重载。无论形参为何种类型,赋值操作符必须定义为成员函数。
一般, 赋值操作符和复合赋值操作符应返回左操作数的引用。返回*this 
下标操作符:下标操作符必须定义为类成员函数。
类定义下标操作符时, 一般需要定义两个版本,一个为非const 成员并返回引用; 另一个为const 成员const引用
智能指针升级版(这节理解的呕心沥血 ) 最后举个code 吧。
成员访问操作符:箭头操作符必须定义为类成员函数。解引用操作符不要求定义为成员。
重载解引用操作符:解引用操作符是一个一元操作符。需要解引用操作符的const 和非const 版本,区别在于返回类型: const成员返回const 引用以防止用户修改基础对象。 
理解: 解引用操作符一般用于对指针值进行操作返回指针指向内存位置的引用值。
          而对类类型重载解引用操作符可以让 该操作符 返回特定的 引用值。EX: 比如 class A 中有一个成员,成员的类型就是 之前定义过的 “智能指针类”的 指针, 即成员指向一个智能指针类的对象, 而该智能对象里面存储的成员(一个指针 下面称为 real_val) 指向的内存才是 class A 想获得的值, 中间的指针指针这一层只是作为管理。 因此可以 通过重载 解引用操作符 是class A 的对象返回 *real_value。
重载箭头操作符:重载箭头操作符 必须返回指向类类型的指针,或则返回 定义了自己的箭头操作符的类类型对象。只能是前面两者之一
      如果返回类型是指针,则内置箭头操作符可用于该指针,编译器对该指针解引用并从结果对象中获取成员(注意: 获取成员是编译自动获取的!!!)
     如果返回类型是类类型的其他对象(或是这种对象的引用),则将递归应用该操作符。编译器检查返回对象所属类型是否具有成员箭头。如果有,就应用那个操作符,没有,就出错。
EX :比如两个类 class A 和 class B , class A 里面重载了箭头操作符 -> , 该操作符返回的值是指向 class B 的一个对象,  A  a_class ; 设 a_class 是 class A 的一个对象。 则 a-> 成员列表某一项 (就是B 对外公开的接口,如果 把A 设为 B 的友元,则列表还能访问private 成员)   ; 意思就是 class A 的对象可以通过箭头操作符 返回一个 class B 的指针 ,而通过这个指针去运行B的成员, 也就是前面 选中的成员类表。 这是返会类类型指针的情况。 再加多个 class C , class  C 中也重载了箭头操作符 -> ,该操作符返回的是class A 的一个对象,而 A B的设置不变, C c_class ,  则 a->成员列表某一项 (以让还是B 对外公开的接口,如果 把A 设为 B 的友元,则列表还能访问private 成员)   。 
贴个代码:
上面的类B 或 类C 的对象:
#ifndef  INT_CLASS_H
#define INT_CLASS_H
#include <iostream>
using std::cout;
using std::endl;

class int_class
{
public :
	int_class (int v): value(v) {}
	int_class (){}

	int getValue()		//这个就是前面解释的B类对象或C类对象 可用的接口
	{
		return value;
	}

private:
	int value;
};
#endif
智能指针类:属于上面的类A 或 类B 
#ifndef  U_PTR_H	
#define  U_PTR_H

#include "int_class.h"  // 析构函数中用到了int_class  ,不完全类型不够用
class HasPtr;
class int_class;

#include <iostream>
using std::cout;
using std::endl;

class U_Ptr
{
	friend class HasPtr;
	int_class *ip;	 //这个类只是起到管理指针的作用,不严格属于上面所讲的类A或B
	size_t use;


	U_Ptr(int_class *p): ip(p), use(1){}
	~U_Ptr(){ 
		cout<<"delete:"<<ip->getValue()<<endl;
		delete ip; 
	}

};

#endif
类A 或 类B 的对象。
#ifndef  HASPTR_H
#define HASPTR_H
#include "U_Ptr.h"

class U_Ptr;
class int_class;

class HasPtr
{
public :
	HasPtr(int_class *ip, int i);

	HasPtr (const HasPtr& hp);

	HasPtr & operator = (const HasPtr&) ;

	~HasPtr ();

	int_class &operator*() {return *ptr->ip;}
	int_class *operator->() {return ptr->ip;}	// 重载-> 运算符,这里是返回类 的指针值,该指针指向int_class
	
	const int_class &operator*() const { return *ptr->ip;}
	const int_class *operator->() const { return ptr->ip; }

private:
	int val;
	U_Ptr *ptr;
};

#endif
上面类的实现文件:
#include<iostream>
#include "HasPtr.h"
#include "U_Ptr.h"


HasPtr & HasPtr::operator=(const HasPtr & rval)
{
	++rval.ptr->use;
	if(--ptr->use==0)
		delete ptr;

	ptr=rval.ptr;
	val=rval.val;

	return *this;
}


HasPtr::HasPtr(int_class *ip,int i): ptr(new U_Ptr(ip)), val(i){}

HasPtr::HasPtr(const HasPtr& hp):ptr(hp.ptr), val(hp.val){ ++ptr->use; }

HasPtr::~HasPtr()
{
	if(--ptr->use == 0) delete ptr;
}
测试文件 main
#include <iostream>
#include "HasPtr.h"

int main()
{

	HasPtr hp1(new int_class(4),3);
	HasPtr hp2(hp1);

	HasPtr hp3(new int_class(5),4);

	cout<<(*hp3).getValue()<<endl;

	cout<<hp3->getValue()<<endl;
	
	return 0;

}


自增操作符 和 自减操作符

定义前自增 / 前自减操作符:
     返回类型: 被增量 或 减量对象的引用

区别操作符的前缀 和 后缀 形式:后缀式操作符函数接受一个额外的int形参。使用时,编译器提供0 作为实参。

调用操作符 和 函数对象 :函数调用操作符必须声明为成员函数。 定义了调用操作符的类,其对象常称为 函数对象

将函数对象用于标准库算法:把原来传递给算法 的判定函数改成 函数对象就可以了。

标准库定义的函数对象:头文件 functional
     1,每个类表示一个给定的操作符。 每个类都定义了应用命名操作的调用操作符。不同的函数对象定义了执行不同操作的函数操作符
      2,表示操作数类型的模板类型 。每个函数对象类都是一个类模板,需要为改模板提供一个类型



函数对象的函数适配器:
     1) 绑定器,一种函数适配器。通过将一个操作数绑定到给定值 而将二元函数对象转换成一元函数对象。
               bind1st : 将给定值绑定到二元函数对象的第一个实参
               bind2nd:   将给定值绑定到二元函数对象的第二个实参              

     2) 求反器, 将谓词函数对象的真值求反。
               not1 : 将一元函数对象的真值求反
               not2 : 将二元函数对象的真值求反
          

转换与类类型:

转换操作符:类成员函数。定义将类类型值转变成其他类型值的转换。
                     转换操作符在类定义体内声明,在保留字operator 之后跟着转换的目标类型:


转换函数 通用形式: operator type()
     type : 内置类型名、类类型名、 由类型别名所定义的名字。一般,不允许转换成数组或函数类型,转换为指针类型 (数据和函数指针) 以及引用类型 是可以。

注意转换函数必须是成员函数,不能指定返回类型,并且形参表必须为空
虽然转换函数不能指定返回类型,但是每个转换函数必须显式返回一个指定类型的值

规则:转换函数一般不应该改变被转换的对象。因此,转换函数应定义为const 成员。
          只能应用一个类类型转换。类类型转换之后不能再跟另一个类类型转换

标准转换可放在类类型转换之前
     使用构造函数执行隐式转换的时候,构造函数的形参类型不必与所提供的类型完全匹配

注意: 一般而言,给出一个类与两个内置类型之间的转换是不好的做法


重载确定和类的实参

转换和操作符:
          1)不要定义互相转换的类。
          2)如果定义了到算术类型的转换,则
                    .  不要定义接受算术类型的操作符的重载版本。 如果需要,转换操作符将转换 所定义的类型的对象。
                    . 不要定义转换到一个以上算术类型的转换。           




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值