• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            隨筆 - 2, 文章 - 0, 評論 - 4, 引用 - 0
            數據加載中……

            在類定義中實現對私有數據成員的隱藏

            上篇文章中,小小展示了下指針的強大威力,也揭示了C++中類的不安全性。

            但在實際應用中,如果你寫的類考慮周全,功能完善的話,類的用戶沒有必要通過這種方式來訪問類的私有成員。同時類的用戶自己也有對安全的訴求,因此也一般不會通過此種非正常方式來隨意訪問類的私有成員。

            但是,這里又要提到“但是在實際應用中”——你也許無法一次寫出一個完全可靠的類,不可避免地會在以后的編碼中逐步對類進行不同程度的修改,有時甚至會大刀闊斧地刪除多余的成員,增加其他新的成員。這時頭文件就會改變,類成員的地址偏移也會發生變化。你需要向其他編碼者更新你的頭文件,其他文件中如果用到你的這個類,那么這些文件就需要重新編譯、連接,很多問題隨之而來。

            現在我們要做的就是最大可能地隱藏數據成員的細節,只在頭文件中展示使用這個類最必要的部分。

            聰明的你一定想到另外再定義一個結構體或類 class MemberData ,把所有數據成員都放到 class MemberData 里面,然后在你的類中聲明一個 class MemberData 的對象作為類的私有數據成員。


            也許你會這樣做:

            /*
             *    memberdata.hpp
             
            */
            #ifndef MEMBERDATA_HPP
            #define MEMBERDATA_HPP


            class MemberData
            {
            private:
                
            int a;
                
            double b;
                
            char c;
                
            //
                friend class MyClass;
            };

            #endif // MEMBERDATA_HPP

            //////////////////////////////////////////////////////////////////////////

            /*
             *    myclass.h
             
            */
            #ifndef MYCLASS_H
            #define MYCLASS_H
            #include 
            "memberdata.hpp"

            class MemberData;

            class MyClass
            {
            public:
                MyClass();
                
            ~MyClass();
            private:
                MemberData members;
            };

            #endif // MYCLASS_H

            但問題是細節隱藏得還不夠深,要提供 myclass.h 必須要連同 memberdata.hpp 一起提供,其他人打開 memberdata.hpp 照樣能看見實際的數據成員。

            還有更好的辦法嗎?將 class MemberData 寫在 myclass.cpp 里,甚至直接將整個 class MemberData 作為 class MyClass 的私有類?就像下面這樣:

            注意下面的代碼是分成頭文件和實現文件兩部分的,需要分開放。不然那句 #endif 會作怪。

            /*
             *    myclass.h
             
            */
            #ifndef MYCLASS_H
            #define MYCLASS_H
            #include 
            "memberdata.hpp"

            class MyClass
            {
            public:
                MyClass();
                
            ~MyClass(){};
            private:
                
            class MemberData;
                MemberData members;
            };

            #endif // MYCLASS_H

            //////////////////////////////////////////////////////////////////////////

            /*
             *    myclass.cpp
             
            */

            #include 
            "myclass.h"

            class MyClass::MemberData
            {
                
            int a;
                
            double b;
                
            char c;
                
            //
                MemberData(int _a=0double _b=0char _c='\0')
                    : a(_a)
                    , b(_b)
                    , c(_c)
                {
                }
            };

            MyClass::MyClass()
            : members(
            11'c')
            {
            }

            好,按照我的要求分成兩部分了,但編譯器看到 MemberData members; 這行時就會提示使用了未定義的 class MemberData。是的,編譯器不認同這樣的代碼,即使我已經在前面給出了 class MemberData 的聲明,即使在 myclass.cpp 里我還專門將 class MemberData 的定義放到構造函數前面。

            雖然在頭文件里只對 class MyClass 的成員做了聲明,但卻是個實實在在的類定義。編譯器看到類定義就會考慮確定這個類中成員的地址偏移,從而進一步確定整個類的大小。而這里的 class MemberData 還是沒有定義的,因此無法確定 members 對象的大小,那么這個類型所占用的內存空間也是無法確定的。編譯器太心急了,雖然它還未看到  myclass.cpp 中 class MemberData 的定義,雖然 class MyClass 仍然未實例化,它就已經想到以后的事情了。

            那么我們考慮將 MemberData members; 這句聲明換成 MemberData* pMembers; 。一個指針,無論是什么類型,總是占用 4 個字節的空間,因此其大小是確定的。

            果然,沒有任何的錯誤,順利通過編譯和連接。

            實際上很多商業代碼就是用類似的做法。不過他們更絕,類的其他使用者在頭文件中連 class MemberData 的聲明都看不到,只看到一個 LPVOID pData 。是的,每個類可能會包含不同的數據,但我們只需要一個指針即可。pData 所指向一個什么樣的類型無所謂,需要的時候用 reinterpret_cast 將指針轉換到相應的類型即可。

            但新的問題隨之而來。pMembers (或 pData )未指向任何實在的內存空間,我們必須在構造函數中為  pMembers 分配空間,否則 MyClass 的數據成員并不存在。既然分配了空間,那就還要在析構函數中釋放空間。為了穩妥,還必須為 class MyClass 編寫拷貝構造函數和賦值函數。

            一個類沒有什么,但如果每寫一個類都要這樣做的話,代碼量將劇增。相比以前我們只需要簡單的 private 一下,那可是麻煩多了。可,我是懶人一個啊。

            懶人自有懶人的辦法,而且一定要緊跟流行趨勢。這幾年流行泛型,我們就用寫個類模板來實現想要的功能。

             1 #ifndef IWONG_IMPLEMENT_HPP
             2 #define IWONG_IMPLEMENT_HPP
             3 namespace iwong {
             4 
             5 //////////////////////////////////////////////////////////////////////////
             6 // noncopyable
             7 template<typename tClass> class Implement_noncopyable
             8 {
             9 public:
            10     typedef typename tClass element_type;
            11     typedef typename element_type* element_type_pointer;
            12     typedef typename element_type& element_type_reference;
            13     typedef typename element_type const& element_type_const_reference;
            14     typedef typename Implement_noncopyable<element_type> this_type;
            15     typedef typename this_type& reference;
            16     typedef typename this_type const& const_reference;
            17 
            18 public:
            19     Implement_noncopyable() : pImp(NewPtr()) {}
            20     ~Implement_noncopyable() { Release(); }
            21 
            22 public:
            23     element_type_pointer get_ptr() { return pImp; }
            24     element_type_reference get() { return *pImp; }
            25     element_type_const_reference get() const { return *pImp; }
            26     element_type_pointer operator->() { return get_ptr(); }
            27 
            28 protected:
            29     Implement_noncopyable(const_reference _other) : pImp(NewPtr(_other)) {}
            30 
            31     virtual const_reference operator=(const_reference _other)
            32     {
            33         ValueCopy(_other);
            34         return *this;
            35     }
            36 
            37 private:
            38     element_type_pointer NewPtr() { return new element_type; }
            39     element_type_pointer NewPtr(const_reference _other) { return new element_type(_other.get()); }
            40     void ValueCopy(const_reference _ohter) { get() = _ohter.get(); }
            41     void Release() { delete pImp; }
            42 
            43 private:
            44     element_type_pointer pImp;
            45 };
            46 
            47 //////////////////////////////////////////////////////////////////////////
            48 // copyable
            49 template<typename tClass> class Implement : public Implement_noncopyable<tClass>
            50 {
            51 public:
            52     typedef typename const Implement<tClass>& const_reference;
            53 
            54 public:
            55     Implement() : Implement_noncopyable() {}
            56     Implement(const_reference _other) : Implement_noncopyable(_other) {}
            57 
            58     const_reference operator=(const_reference _other)
            59     {
            60         Implement_noncopyable::operator=(_other);
            61         return *this;
            62     }
            63 };
            64 
            65 //////////////////////////////////////////////////////////////////////////
            66 
            67 // namespace iwong
            68 
            69 #endif // IWONG_IMPLEMENT_HPP

            在這個類模板里,我們封裝了堆空間的分配和釋放,還加上了一些必要的操作。
            Implement::get() 返回數據類的對象的引用;
            Implement::get() const 返回數據類的對象的常引用;
            Implement::get_ptr() 返回數據類的指針;
            Implement::operator->() 返回數據類的指針,是為某些我這樣的懶人準備的,用的時候少寫幾個字母而已。但注意由于這里重載的是 -> 操作符,因此實際得到的是數據類的對象!于是其功能同 Implement::get() 是一樣的。

            class Implement_noncopyable 的對象除不可拷貝外,其他用法同 class Implement 一樣。

            class MyClassImp 的細節都隱藏在 cpp 文件中,只要不公開 cpp 實現,從外部是根本沒法進行直接訪問的。

            下面是一個使用實例:

            //////////////////////////////////////////////////////////////////
            // MyClass.h

            #ifndef MYCLASS_H
            #define MYCLASS_H
            #include 
            <Implement.hpp>
            using namespace iwong;

            class MyClass
            {
            public:
                MyClass(){}
                
            ~MyClass(){}

                
            int GetA();
                
            void SetA();
            private:
                
            class MyClassImp;
                Implement
            <MyClassImp> Imp;
            };

            #endif // MYCLASS_H


            ///////////////////////////////////////////////////////////////////
            // MyClass.cpp

            #include 
            "MyClass.h"

            MyClass::MyClass()
            {}

            class MyClass::MyClassImp
            {
            public:
                
            int a;
                
            int* b;
                
            double c;
                
            char d;

                MyClassImp()
                    : a(
            0)
                    , b(
            new int(0))
                    , c(
            0.0)
                    , d(
            'd')
                {
                }

                
            /*
                 *    若在 Imp 類中定義了指針,并為其分配了堆空間
                 *    在析構函數中仍然需要釋放這個指針指向的堆空間
                 
            */
                
            ~MyClassImp() { delete b; }
            };

            int MyClass::GetA()
            {
                
            return Imp.get().a;
            }

            void MyClass::SetA(int _a)
            {
                Imp
            ->= _a;
            }

            需要的注意的是,類模板 Implement 中雖然封裝了堆空間的分配和釋放操作,但這是針對 class MyClassImp 的。而對于 class MyClassImp 中的數據成員,仍然需要自行進行空間的分配和釋放,這一點同以前是沒有兩樣的。

            posted on 2008-08-11 01:32 iwong 閱讀(1017) 評論(0)  編輯 收藏 引用

            国产成人久久精品一区二区三区| 国内精品久久久人妻中文字幕| 精品国产乱码久久久久久呢 | 国产精品嫩草影院久久| 久久婷婷人人澡人人| 亚洲AV无一区二区三区久久| 久久人人爽人爽人人爽av| 亚洲精品乱码久久久久久蜜桃| 久久99精品国产一区二区三区 | 99精品国产99久久久久久97| 99热热久久这里只有精品68| 久久天天婷婷五月俺也去| 94久久国产乱子伦精品免费| 久久超乳爆乳中文字幕| 狠狠色丁香久久婷婷综合图片| 狠狠色丁香婷婷久久综合不卡| 精品久久久久久久久久中文字幕| 狠狠色噜噜狠狠狠狠狠色综合久久 | 久久久青草青青亚洲国产免观| 久久精品天天中文字幕人妻| 精品久久久久久国产牛牛app | 久久精品这里热有精品| 久久人人爽人人人人片av| 国产精品成人精品久久久| 国产精品一区二区久久国产 | 亚洲乱码中文字幕久久孕妇黑人| 狠狠精品干练久久久无码中文字幕| 久久久久亚洲AV成人网人人网站| 久久国产午夜精品一区二区三区| 中文字幕成人精品久久不卡| 伊人久久精品无码二区麻豆| 日韩欧美亚洲综合久久| 香蕉久久影院| 久久亚洲AV无码精品色午夜| 久久精品成人免费国产片小草| 久久久91精品国产一区二区三区| 久久婷婷五月综合色奶水99啪| 久久福利青草精品资源站免费 | 久久这里只有精品首页| 色天使久久综合网天天| 久久国产影院|