• C++ Primer 学习笔记_53_类和数据抽象 --友元、static员


    分类

    --友元、static成员



    一、友元

        友元机制同意一个类将对其非公有成员的訪问权授予指定的函数或类(对未被授权的函数或类,则阻止其訪问);友元的声明以keywordfriend開始,可是它仅仅能出如今类定义的内部

    友元声明能够出如今类中的不论什么地方:友元不是授予友元关系的那个类的成员,所以它们不受其声明出现部分的訪问控制影响。

    【最佳实践】

        通常。将友元声明成组的放在类定义的開始或结尾是个好主意


    1、友元关系:一个样例

        如果一个窗体管理类Window_Mgr可能须要訪问由其管理的Screen对象的内部数据。Screen应该同意其訪问自己的私有成员:

    class Screen
    {
        friend class Window_Mgr;
    };
    

    Window_Mgr的成员能够直接引用Screen的私有成员:

    Window_Mgr &
    Window_Mgr::relocate(Screen::index x,Screen::index c,Screen &s)
    {
        s.height += r;
        s.width += c;
        return *this;
    }

    如果缺少友元声明,这段代码将会出错:将不同意使用形參sheightwidth成员。

    注:友元能够是普通的非成员函数,或前面定义的其它类的成员。或整个类


    2、使其它类的成员函数成为友元

    class Screen
    {
        //Screen指定仅仅同意relocate成员訪问:
        //函数名必须用该函数所属的类名字加以限定!
        friend Window_Mgr &Window_Mgr::relocate(Window_Mgr::index,
                                                Window_Mgr::index,
                                                Screen &);
    };
    

    3、友元声明与作用域

        须要注意友元声明与友元定义之间的互相依赖。

    在前面的样例中,Window_Mgr必须先定义。

    否则,Screen类就不能将一个Window_Mgr函数指定为友元。然而,仅仅有在定义类Screen之后,才干定义relocate函数—— 毕竟,它被设为友元是为了訪问类Screen的成员。

        更一般地讲,必须先定义包括成员函数的类,才干将成员函数设为友元

    还有一方面,不必预先声明类和非成员函数来将它们设为友元。

        友元声明将已命名的类或非成员函数引入到外围作用域中。此外,友元函数能够在类的内部定义,该函数的作用域扩展到包围该类定义的作用域

    class X
    {
        friend class Y;
        friend void f()
        {
    
        }
    };
    
    class Z
    {
        Y * ymem;
        void g()
        {
            return ::f();
        }
    };
    

    4、重载函数与友元关系

        类必须将重载函数集每一个希望设为友元的函数都声明为友元:

    class Screen
    {
        friend std::ostream& storeOn(std::ostream &, Screen &);
        // ...
    };
    
    //将该函数作为Screen的友元
    extern std::ostream& storeOn(std::ostream &, Screen &);
    
    //该函数对Screen没有特殊的訪问权限
    extern BitMap& storeOn(BitMap &, Screen &);
    

    //P398 习题12.34/35
    class Sales_item
    {
        friend Sales_item add(const Sales_item &item1,
                              const Sales_item &item2);
        friend std::istream &input(Sales_item &,std::istream &);
    public:
    
        bool same_isbn(const Sales_item &rhs) const
        {
            return rhs.isbn == isbn;
        }
    
        Sales_item():units_sold(0),revenue(0) {}
    
    private:
        std::string isbn;
        unsigned units_sold;
        double revenue;
    };
    
    Sales_item add(const Sales_item &item1,const Sales_item &item2)
    {
        if (item1.same_isbn(item2))
        {
            Sales_item item;
            item.isbn = item1.isbn;
            item.units_sold = item1.units_sold + item2.units_sold;
            item.revenue = item1.revenue + item2.revenue;
            return item;
        }
    
        return item1;
    }
    
    std::istream &input(Sales_item &item,std::istream &in)
    {
        in >> item.isbn >> item.units_sold >> item.revenue;
    
        return in;
    }
    

    二、static类成员

        通常,static数据成员存在于类类型的每一个对象中。static数据成员独立于该类的随意对象而存在:每一个static数据成员是与类关联的对象,并不与该类的对象相关联

        正如类能够定义共享的static数据成员一样,类也能够定义static成员函数。static成员函数没有this形參,它能够直接訪问所属类的static成员,可是不能直接使用static成员


    1、使用类的static成员的长处

        1)static成员的名字是在类的作用域中,因此能够避免与其它类的成员或全局对象名字冲突

        2)能够实施封装static成员能够是私有成员,而全局对象不能够。

        3)通过阅读程序easy看出static成员是与特定类关联的。这样的可见性可清晰地显示程序猿的意图。


    2、定义static成员

       在成员声明前加上keywordstatic将成员设为staticstatic成员遵循正常的公有/私有訪问规则:

    class Account
    {
    public:
        void applyint()
        {
            amount += amount * interestRate;
        }
    
        static double rate()
        {
            return interestRate;
        }
        static void rate(double);
    
    private:
        std::string owner;
        double amount;
    
        //interestRate 对象的值。为Account类型的全体对象所共享
        static double interestRate;
        static double initRate();
    };
    

    3、使用类的static成员

    能够通过作用域操作符从类直接调用static成员,或者通过对象引用指向该类类型对象的指针间接调用。

        Account ac;
        Account *pac = ∾
        double rate;
        rate = ac.rate();
        rate = pac -> rate();
        rate = Account::rate();
    

    static成员函数

        Account类有两个名为ratestatic成员函数,当中一个定义在类的内部。当我们在类的外部定义static成员时,无须反复指定static保留字,该保留字仅仅出如今类定义体内部的声明处:

    void Account::rate(double newRate)
    {
            interestRate = newRate;
    }
    

       1static成员是类的组成部分但不是不论什么对象的组成部分,因此,static成员函数没有this指针。

       2、由于static成员不是不论什么对象的组成部分,所以static成员函数不能被声明为 const。毕竟,将成员函数声明为const就是承诺不会改动该函数所属的对象。

       3、最后,static成员函数也不能被声明为虚函数(后面介绍)。

    //P400 习题12.38/39/40
    class Foo
    {
    public:
        Foo(int Ival):ival(Ival){}
        Foo():ival(0){}
    
        int get()
        {
            return ival;
        }
    
    private:
        int ival;
    };
    
    class Bar
    {
    public:
        static Foo FooVal()
        {
            ++ callsFooVal;
            return foo;
        }
    
    
    private:
        static int ival;
        static Foo foo;
        static int callsFooVal;
    };
    
    int Bar::ival = 0;
    Foo Bar::foo = Foo();
    int Bar::callsFooVal = 0;
    

    static数据成员

        static数据成员必须在类定义体的外部定义(正好一次)。不像普通数据成员,static 成员不是通过类构造函数进行初始化,而是应该在定义时进行初始化

        保证对象正好定义一次的最好办法,就是将static数据成员的定义放在包括类非内联成员函数定义的文件里

    double Account::interestRate = initRate();
    

    像使用随意的类成员一样,在类定义体外部引用类的static成员时,必须指定成员是在哪个类中定义的。然而,statickeyword仅仅能用于类定义体内部的声明中,定义不能标示为static


    1、特殊的整型const static成员

       仅仅要初始化式是一个常量表达式,整型const static 数据成员就能够在类的定义体中进行初始化:

    class Account
    {
    public:
        static double rate()
        {
            return interestRate;
        }
        static void rate(double);
    
    private:
        //or: static const int period = 30;
        const static int period = 30;
        double daily_tb1[period];
    };
    const int Account::period;      //OK
    const int Account::period = 30; //Error

    【注意:】const static数据成员在类的定义体中初始化时。该数据成员仍必须在类的定义体之外进行定义

    在类内部提供初始化式时,成员的定义就不能再指定初始值了;


    2static成员并非类对象的组成部分

    由于static数据成员不是不论什么对象的组成部分,所以它们的一些使用方式对于非static数据成员而言是不合法的

       1static数据成员的类型能够是该成员所属的类类型。非static成员被限定声明为其自身类对象的指针或引用:

    class Bar
    {
    public:
        //...
    
    private:
        static Bar mem; //OK
        Bar *mem1;
        Bar &mem2;
        Bar mem3;   //Error
    };
    

         2static数据成员能够用作默认实參:

    class Screen
    {
    public:
        Screen clear(char = bkground);
    
    private:
        static const char bkground = '#';
    };
    

    static数据成员不能用作默认实參,由于它的值不能独立于所属的对象而使用。

    //P402 习题12.42
    class Example
    {
    public:
        static double rate;
    
        static const int vecSize = 20;
        static vector<double> vec;
    };
    
    const int Example::vecSize;
    double Example::rate = 6.5;
    vector<double> Example::vec(vecSize);
    

    //P400 “拓展”习题12.37
    class Account
    {
    public:
    
        Account(const std::string &name = "",double Amount = 0):
            owner(name),amount(Amount) {}
    
    
        void applyint()
        {
            amount += amount * interestRate;
        }
    
        static double rate()
        {
            return interestRate;
        }
        static void rate(double);
    
        double deposit(double Amount)
        {
            amount += Amount;
            return amount;
        }
    
        bool withdraw(double Amount)
        {
            if (Amount > amount)
            {
                return false;
            }
            else
            {
                amount -= Amount;
                return true;
            }
        }
    
        double getBalance()
        {
            return amount;
        }
    
    private:
        std::string owner;
        double amount;
    
        static double interestRate;
        static double initRate();
    };
    
    void Account::rate(double newRate)
    {
        interestRate = newRate;
    }
    
    double Account::interestRate = 2.5;
    double Account::initRate()
    {
        return 2.5;
    }

    版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 相关阅读:
    九、运行时内存篇——方法区
    十三、垃圾回收篇——垃圾回收器
    八、运行时内存篇——堆
    十四、JVM性能调优案例——OOM
    一、设计模式一句话总结
    十、对象的内存布局篇
    十一、执行引擎篇
    七、运行时内存篇——虚拟机栈
    工厂方法模式
    外观模式
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4889919.html
Copyright © 2020-2023  润新知