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


    友元函数介绍

    在程序设计中,加入类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");
    }
    View Code

    友元函数

    友元函数应用比较多,常用的场合有:
    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");
    }
    View Code

    运算符重载

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

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

    函数和返回值

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

    运算符实例

    +和-运算符重载

    #include <iostream>
    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<<endl;
        //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;调用成员函数时,第一个操作数必须是对象,因为第一个操作数还有调用成员函数的功能
    
    
    }
    View Code

    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<<a.operator ++(2);//显式调用成员函数operator ++(2),后缀表达式  
    cout<<a.operator ++();//显式调用成员函数operator ++(),前缀表达式  
    cout<<operator --(b,2);//显式调用友元函数operator --(2),后缀表达式  
    cout<<operator --(b);//显式调用友元函数operator --前缀表达式
    } 
    View Code

    在前置++(++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;//此处为对象成为左值,必须是对象引用,如果返回值为对象变量的时候,编译器不知道如何转化。
    }
    View Code

    运算符[]中的重载方式:只能使用成员函数重载。
    函数名: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<<p.x<<endl;  
        return cout;  
    }  
    istream& operator>>(istream& cin,Point& p)  
    {  
        cin>>p.x;  
        return cin;  
    }
    Point a(1);  
    Point b(2);  
    cin>>a>>b;  
    cout<<a<<b<<endl;    
    View Code

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

    函数名:
    输出流: 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,对象引用)
      成员函数要求是有对象调用,则第一个参数必须是类的对象,但是<<和>>第一个参数是流的对象引用。不能使用成员函数

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    windows下 文件资源管理器 的操作
    Visual Studio Code 折叠代码快捷键
    windows 10 取消alt+tab的预览功能
    String.prototype.replace
    Webpack的tapable 为什么要使用 new Funtion 来生成静态代码
    Visual Studio Code 断点调试Nodejs程序跳过node内部模块(internal modules)
    【社群话题分享】有哪些奇葩的技术人员考核方式?
    工信部要求应用商店上新 App 检查 IPv6,这里有一份 IPv6 快速部署指南
    读完这篇文章,5G 就没有秘密了
    双剑合璧——掌握 cURL 和 Dig 走天涯
  • 原文地址:https://www.cnblogs.com/polly333/p/4705665.html
Copyright © 2020-2023  润新知