友元函数、类和运算符重载

友元函数介绍

在程序设计中,加入类A想访问类B中的私有成员和私有函数时,为了保持程序的封装性,又让其有共享性,就用到了友元类和友元函数。

友元类



class A
{ 
    friend class B ;
public :
    void  Display() { cout << x << endl ; } ;
private :
    int  x ;
} ;
//上面在类A声明了它的友元类是B,所以类B可以访问A中私有成员和私有函数
class  B
{ 
public :
    void Set ( int i ) { Aobject . x = i ; } 
    void Display ()  { Aobject . Display () ; }
     //调用了类A的私有成员
private :
    A  Aobject ;//声明了类A的对象
} ;
void main()
{
    B  Bobject ;
    Bobject . Set ( 100 ) ;
    Bobject . Display () ;
    system("pause");
}

友元函数

友元函数应用比较多,常用的场合有:
1. 运算符重载的某些场合需要使用友元。
2. 两个类需要共享数据的时候。

class Test2
{
public:
    //友元函数的特点是:有一个参数是友元类的指针或引用.
    //友元函数是类外函数,所以声明放在公有段或者私有段没有区别。
    friend int OpMem(Test2 *p, int a); //友元函数,中有类对象指针,通过它访问类中私有成员
    Test2(int a, int b)
    {
        this->a = a;
        this->b = b;
    }
    int getA()
    {
        return this->a;
    }
protected:

private:
    int a ;
    int b;
};


int OpMem(Test2 *p, int a)
{
    p->a = a;//获取类中私有成员
    return 0;
}

void main()
{
    Test2 t1(1, 2);
    t1.getA();
    OpMem(&t1, 10);//直接调用友元函数,毕竟友元函数不是成员函数
    system("pause");
}

运算符重载

运算符重载是指:为了实现类的多态性(一个函数名有多重含义),运算符与类结合,产生新的含义。
怎么实现运算符的重载?
方式:类的成员函数 或 友元函数(类外的普通函数)
规则:不能重载的运算符有 . 和 .* 和 ?: 和 :: 和 sizeof
友元函数和成员函数的使用场合:一般情况下,建议一元运算符使用成员函数,二元运算符使用友元函数。
1、运算符的操作需要修改类对象的状态,则使用成员函数。如需要做左值操作数的运算符(如=,+=,++)
2、运算时,有数和对象的混合运算时,必须使用友元
3、二元运算符中,第一个操作数为非对象时,必须使用友元函数。如输入输出运算符<<和>>
具体规则如下:

运算符 建议使用
所有一元操作符 成员函数
= 、()、[]、-> 必须是成员函数
+=、-=、/=等带=的运算符 成员函数
+、-、*等二目运算符 友元函数
<<>> 必须是友元函数

函数和返回值

  1. 如果返回值出现在=左边,作为左值(也可以作为右值),必须是非const引用。
  2. 如果返回值出现在=右边,只能作为右值,返回const引用或者const值(只读)。

运算符实例

+和-运算符重载

#include 
using namespace std;
class PointTest
{
private: 
    double pointX;  
public:
    PointTest(int x1)
    {
        pointX=x1;

    }
    void PrintResult();
    const PointTest operator +(const PointTest &obj);//使用成员函数重载运算符
    friend const PointTest operator -(const PointTest &obja,const PointTest & objb);//友元函数

};
#pragma region 成员函数

const PointTest PointTest::operator+(const PointTest &obj)
{
    return PointTest(this->pointX + obj.pointX);
}

void PointTest::PrintResult()
{
    cout<<this->pointX<//system("pause");
    getchar();
}
#pragma endregion
//友元函数重载+
 const PointTest operator-(const PointTest &obja,const PointTest & objb)
 {
    return PointTest(obja.pointX-objb.pointX);
 }

int main()

{
    PointTest a(1);
    PointTest b(2);
    PointTest c = a+b;//调用成员函数
    c.PrintResult();
    c =c-b;
    c.PrintResult();
    a-1;//正确,先调用类型转换函数,把1变成对象,之后调用友元函数
    a+1;//同上,但是参数必须是const类型,才编译通过
    1-a;
    //1+a;调用成员函数时,第一个操作数必须是对象,因为第一个操作数还有调用成员函数的功能


}

1、由于+ -都是出现在=号的右边,如c=a+b,即会返回一个右值,可以返回const型值
2、后几个表达式讨论的就是,数和对象混合运算符的情况,一般出现这种情况,常使用友元函数

单目运算符++ 、–

class Point    
{    
private:    
    int x;   
public:    
    Point(int x1)  
    {   x=x1;}    
    Point operator++();//成员函数定义自增  
    const Point operator++(int x); //后缀可以返回一个const类型的值  
    friend Point operator--(Point& p);//友元函数定义--  
    friend const Point operator--(Point& p,int x);//后缀可以返回一个const类型的值  
};    

Point Point::operator++()//++obj  
{  
    x++;  
    return *this;  
}  
const Point Point::operator++(int x)//obj++  
{  
    Point temp = *this;  
    this->x++;  
    return temp;  
}  
Point operator--(Point& p)//--obj  
{  
    p.x--;  
    return p;  
         //前缀形式(--obj)重载的时候没有虚参,通过引用返回*this 或 自身引用,也就是返回变化之后的数值  
}  
const Point operator--(Point& p,int x)//obj--  
{  
    Point temp = p;  
    p.x--;  
    return temp;  
         // 后缀形式obj--重载的时候有一个int类型的虚参, 返回原状态的拷贝  
}  
int main()
{
Point b(2);  
a++;//隐式调用成员函数operator++(0),后缀表达式  
++a;//隐式调用成员函数operator++(),前缀表达式  
b--;//隐式调用友元函数operator--(0),后缀表达式  
--b;//隐式调用友元函数operator--(),前缀表达式  
cout<operator ++(2);//显式调用成员函数operator ++(2),后缀表达式  
cout<operator ++();//显式调用成员函数operator ++(),前缀表达式  
cout<<operator --(b,2);//显式调用友元函数operator --(2),后缀表达式  
cout<<operator --(b);//显式调用友元函数operator --前缀表达式
} 

在前置++(++a)和后置++(a++)中,前后缀仅从函数名(operator++)无法区分,只能有参数区分,这里引入一个虚参数int x,x可以是任意整数。
重载运算符[]
在这里将讲到返回对象引用。

class Point    
{    
private:    
    int x[5];   
public:    
    Point()  
    {  
        for (int i=0;i<5;i++)  
        {  
            x[i]=i;  
        }  
    }   
    int& operator[](int y);  //声明的返回对象的引用
};    
int& Point::operator[](int y)  
{  
    static int t=0;  
    if (y<5)  
    {  
        return x[y];  
    }  
    else  
    {  
        cout<<"下标出界";  
        return t;  
    }     
}  


int main()
{
Point p;
int a = p[3];//此处为右值,可以为变量,也可以为引用。
a[2] = 3;//此处为对象成为左值,必须是对象引用,如果返回值为对象变量的时候,编译器不知道如何转化。
}

运算符[]中的重载方式:只能使用成员函数重载。
函数名:operator
参数表:一个参数,且仅有一个参数,该参数设定了下标值,通常为整型,但是也可以为字符串( 看成下标)。
返回值: 返回的值可以做左值,也可以做右值,则必须使用返回引用。
难点重载运算符<<,>>

这里写代码片class Point    
{    
private:    
    int x;   
public:    
    Point(int x1)  
    {   x=x1;}   
    friend ostream& operator<<(ostream& cout,const Point& p);//使用友元函数重载<<输出运算符  
    friend istream& operator>>(istream& cin,Point& p);//使用友元函数重载>>输出运算符  
}; 
//对于<<这种运算符,格式相对比较固定,返回类型还有参数。   
ostream& operator<<(ostream& cout,const Point& p)  
{  
    cout<return cout;  
}  
istream& operator>>(istream& cin,Point& p)  
{  
    cin>>p.x;  
    return cin;  
}
Point a(1);  
Point b(2);  
cin>>a>>b;  
cout<

语法:
重载方式:只能使用友元函数重载 且 使用三个引用&

函数名:
输出流: operator<<(参数表)
输入流:operator>>(参数表)

  • 输出流
    输出流: 必须是两个参数:对输出流ostream& 和 对象第一个操作数cout,定义在文件iostream中,是标准类类型ostream的对象的引用。
    如:ostream& cout,const Point& p
    ostream& operator<<(ostream& cout,const 对象引用)
  • 输入流
    必须是两个参数:对输入流ostream& 和 对象
    第一个操作数是cin,定义在文件iostream,实际上是标准类类型istream的对象的引用
    如:instream& cin,const Point& p
    istream& operator>>(istream& cin,对象引用)
    成员函数要求是有对象调用,则第一个参数必须是类的对象,但是<<和>>第一个参数是流的对象引用。不能使用成员函数

你可能感兴趣的:(C++)