C++友元与运算符重载

友元:

采用类的机制后实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,成员函数一般定义为公有的,依此提供类与外界间的通信接口。但是,有时需要定义一些函数,这些函数不是类的一部分,但又需要频繁地访问类的数据成员,这时可以将这些函数定义为该函数的友元函数。除了友元函数外,还有友元类,两者统称为友元。友元的作用是提高了程序的运行效率(即减少了类型检查和安全性检查等都需要时间开销),但它破坏了类的封装性和隐藏性,使得非成员函数可以访问类的私有成员。

友元函数:

 友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下:
       friend 类型 函数名(形式参数);

       友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数。
       一个函数可以是多个类的友元函数,只需要在各个类中分别声明。
       友元函数的调用与一般函数的调用方式和原理一致。

eg:

主函数:

// youyuan.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "UncleWang.h"
#include "Family.h"

void GetUncle_Heart(const UncleWang& uncle);


int _tmain(int argc, _TCHAR* argv[])
{
	UncleWang uncle1;
	uncle1.Hobby();
	GetUncle_Heart(uncle1);
	getchar();

	return 0;
}

void GetUncle_Heart(const UncleWang& uncle)
{
	cout << "要俘获的人的心的名:" << uncle.name  << endl;
	cout << "要俘获的人的心的龄:" << uncle.age  << endl;
}



 #include "UncleWang.h"

#pragma once
#define NAMELEN 10
class UncleWang
{
	char* name;
	int age;
public:
	UncleWang();
	void Hobby();
	friend void GetUncle_Heart(const UncleWang& uncle);
	~UncleWang();
};

UncleWang.cpp

#include "stdafx.h"
#include "UncleWang.h"


void UncleWang::Hobby()
{
	cout << "我是隔壁" << this->name << ",我的年龄是:" << this->age << endl;
}

UncleWang::UncleWang()
{
	this->name = new char[NAMELEN];
	memset(this->name,0,NAMELEN);
	strcpy(this->name,"老王");
	this->age = 35;

}


UncleWang::~UncleWang()
{
	delete[] name;
}

这就是友元函数。

友元类:

  友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。       
       当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。定义友元类的语句格式如下:
       friend class 类名;
       其中:friend和class是关键字,类名必须是程序中的一个已定义过的类。

       例如,以下语句说明类B是类A的友元类:
       class A
       {
              …
       public:
              friend class B;
              …
       };
       经过以上说明后,类B的所有成员函数都是类A的友元函数,能存取类A的私有成员和保护成员。

       使用友元类时注意:
             (1) 友元关系不能被继承。
             (2) 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
             (3) 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明

A被B申明为友元类以后,在A的函数里,若B为参数,可直接访问B的成员与成员函数。

例:

main.cpp

// youyuan.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "UncleWang.h"
#include "Family.h"

void GetUncle_Heart(const UncleWang& uncle);


int _tmain(int argc, _TCHAR* argv[])
{
	UncleWang uncle1;
	//uncle1.Hobby();
	//GetUncle_Heart(uncle1);
	Family f;
	uncle1.Uncle_visit_Family(f);
	getchar();

	return 0;
}

void GetUncle_Heart(const UncleWang& uncle)
{
	cout << "要俘获的人的心的名:" << uncle.name  << endl;
	cout << "要俘获的人的心的龄:" << uncle.age  << endl;
}



family.h

#pragma once
#define NAMELEN 10
class Family
{
	char* mother;
	char* father;
	char* me;
public:
	friend class UncleWang;
	Family();
	~Family();
	void Show();
};

family.cpp

#include "stdafx.h"
#include "Family.h"

void Family::Show()
{
	cout << "家庭成员:" << this->father<<" " << this->mother<<" " << this->me;
}

Family::Family()
{
	this->father = new char[NAMELEN];
	this->mother = new char[NAMELEN];
	this->me = new char[NAMELEN];
	strcpy(this->father,"父亲");
	strcpy(this->mother, "母亲");
	strcpy(this->me, "我");
}


Family::~Family()
{
	delete[] father;
}

 UncleWang.h

#pragma once
#define NAMELEN 10
#include "Family.h"
class UncleWang
{
	char* name;
	int age;
public:
	UncleWang();
	void Hobby();
	friend void GetUncle_Heart(const UncleWang& uncle);
	void Uncle_visit_Family(Family uncle);
	~UncleWang();
};

  UncleWang.cpp

#include "stdafx.h"
#include "UncleWang.h"


void UncleWang::Hobby()
{
	cout << "我是隔壁" << this->name << ",我的年龄是:" << this->age << endl;
}

void UncleWang::Uncle_visit_Family(Family f)
{
	cout << this->name << "拜访了" <<f.father << "一家"<<endl;
	cout << "f的一家:";
	f.Show();
}

UncleWang::UncleWang()
{
	this->name = new char[NAMELEN];
	memset(this->name,0,NAMELEN);
	strcpy(this->name,"老王");
	this->age = 35;

}


UncleWang::~UncleWang()
{
	delete[] name;
}

运算符重载:

运算符重载的本质为函数重载,但有一定的规则需要遵循。

双目运算符尽量声明为友元,在类外写。cin cout 必须申明为友元。

第一点:

这里我们必须注意的是,重载的参数个数必须与运算符原意的个数一致。比如+号的参数就是左加数和右加数两个。那么当我们重载加号时也要保证有左右两个加数作为参数。

第二点:

重载作为普通函数时:

单目:右操作数是形参;双目:左边形参作为运算符左操作数,右边形参是右操作数。

重载作为类的成员函数时:

如果运算符参数只有一个,那么不需要写参数;如果运算符参数有两个,那么只需要写一个参数。

因为类的成员函数默认有一个this指针。它会直接指向类本身。换句话说,当我们写出运算符重载时,有一个参数就已经被this指针包含了。

单目运算符,this所指向运算符右参数,因为单目运算符的参数一般都在右边。

双目运算符,this指向运算符左参数。

以减号为例,两个时间类a和b相减时。如果是a - b,那么this指针指向a,反之则指向b。在声明函数时,我们只需要写右参数即可。a - b的话只需要写 int operator-(Time b);

当然,单目运算符由于只有一个参数,且该参数被this所指向,那么我们无需声明任何参数即可。

(二).返回值
运算符的返回值类型取决于该重载函数的作用是什么。

比如a + b的作用是得到一个加数,那么返回值就是a+b的值。a += b的作用是让a的值改变,既然是让参数a的值改变,那么就无需返回值。

还有就是如果我们需要运算符支持多次操作那么也需要返回值。比如流插入运算符<<。我们可能需要多次进行插入,像cout << a << b << c;之类就需要返回流ostream本身以便于之后的流插入工作。

<< >>运算符重载:

#include <iostream>
#include <string> 
#include <cstring> 
using namespace std;
class String
{
private:
    string str;   
public:
	String(string s){str=s;}
	char & operator[](int i);
	friend ostream & operator<<(ostream & cout, const String & st);
    friend istream & operator>>(istream & cin, String & st);
};

char &String::operator[](int i)
{
    return str[i];
}
ostream &operator<<(ostream & cout, const String &st)
{
    cout << st.str;
    return cout;
}

istream &operator>>(istream & cin, String &st)
{
    char temp[40];
    is.get(temp, 40);
    if (cin)
        st = temp;
    while (cin && cin.get() != '\n')
        continue;
    return cin;
}

 =运算符重载

#include <cstring>
#include <sstream>
class HugeInteger{
public:
	HugeInteger& operator=(const HugeInteger &h);
private:
    string str;
};

HugeInteger& HugeInteger::operator=(const HugeInteger &h)
{
    this->str=h.str;
    return *this;
}

猜你喜欢

转载自blog.csdn.net/makabaka12138/article/details/128538129