• <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>
            隨筆-4  評論-40  文章-117  trackbacks-0
            (轉(zhuǎn)載)


            Ogre源碼剖析: 任意類型類 Any

             

            有些時(shí)候我們可能想做這樣一件事:

            float f = 1.f;

            int n = 2;

            std::vector<X> myContainer;           // X是一個(gè)虛構(gòu)的用戶定義類型

            myContainer.pushback(X(f));

            myContainer.pushback(X(n));

             

            我們想在一個(gè)容器里保存兩種乃至多種不同的數(shù)據(jù)類型。

            但是,顯然普通的模板參數(shù)如std::vector<int>,或者std::vector<float>都無法滿足我們的需求。

             

            也可能存在下面這樣的情況:

            float fVal = 1.f;

            X xMsg = fVal;

            PushMessage(xMsg); // PushMessage發(fā)送了一個(gè)異步消息

             

            // 另一個(gè)地方(可能是另一個(gè)線程),回調(diào)函數(shù)被調(diào)用:

            OnMessageXXX(X xMsg)

            {

                     float f = static_cast<float>(xMsg);

                     // do something with value f

            }

             

            針對這種需求,我們想要一種類型,它可以接受任意的類型,并在需要的時(shí)候把我們放入的真正的類型取出來,它可以被放置到容器中,可以被拷貝,以及串行化(前提要求被置入其中的類型可以被輸出到標(biāo)準(zhǔn)輸出流–std::ostream當(dāng)中)。

             

            如何設(shè)計(jì)這種類型呢?

             

            一種容易想到的方案如下:

             

            class Any {

            public:

            enum {

                              ANYTYPE_INT,

                              ANYTYPE_FLOAT,

                              ANYTYPE_LONG,

                              // … other data types

            };

             

            union {

                               int nIntData;

                               float fFloatData;

                               long lLongData;

                               // … other data types

            };

             

            Any(int nData) : nIntData(nData), m_type(ANYTYPE_INT) {}

            Any(long lData) : lLongData(lData), m_type(ANYTYPE_LONG) {}

            Any(float fData) : fFloatData(fData), m_type(ANYTYPE_FLOAT) {}

            // … other constructors

            };

             

            現(xiàn)在我們可以這樣寫:

            Int nData = 3;

            Any myVal(nData);

             

            為了讓Any可以被賦值,我們需要給Any提供一個(gè)operator =。

             

            Any& Any::operator = (const Any& rhs)

            {

                     if (&rhs == this)

                               return *this;

                     m_type = rhs.m_type;

                     switch(rhs.m_type)

            {

            case ANYTYPE_INT:

                     nIntData = rhs.nIntData;

                     break;

            case ANYTYPE_LONG:

            lLongData = rhs.lLongData;

            break;

            case ANYTYPE_FLOAT:

                     fFloatData = rhs.fFloatData;

                     break;

            default:

                     ASSERT(0);

            };

            }

             

            這樣Any就有了相互之間被賦值的能力了,如下:

             

            Any myVal1(1);

            Any myVal2(5.6f);

            myVal1 = myVal2;

             

            然而我們還希望Any可以直接用各種數(shù)據(jù)類型賦值。因此,針對需要支持的每一種數(shù)據(jù)類型,都應(yīng)該重載一個(gè)operator =。如下:

             

            Any& operator = (int inData)

            {

                     nIntData = inData;

                     m_type = ANYTYPE_INT;

                     return *this;

            }

             

            Any& operator = (long inData)

            {

                     lLongData = inData;

                     m_type = ANYTYPE_LONG;

                     return *this;

            }

             

            等等。

            當(dāng)然如果嫌這種寫法過于冗長,出于節(jié)省代碼的考慮,我們可以使用一個(gè)宏來代替:

            #define OPEQUAL(type, typeID, varName) \

                     Any& operator = (type nData) \

                     { \

                               varName = paramName; \

                               m_type = typeID; \

                               return *this;

                     }

            這樣就可以將上述代碼轉(zhuǎn)換成:

            OPEQUAL(int, ANYTYPE_INT, nIntData);

            OPEQUAL(long, ANYTYPE_LONG, lLongData);

            OPEQUAL(float, ANYTYPE_FLOAT, fFloatData);

             

            此外,作為一種數(shù)據(jù)的承載,我們還希望在需要的時(shí)候把實(shí)際的數(shù)據(jù)取出來,因此我們需要重載一系列獲取函數(shù):

             

            operator int() const { return nIntData; }

            operator long() const { return lLongData; }

            operator float() const { return fFloatData; }

             

            有了這些類型operator之后,當(dāng)需要從Any中取出我們想要的數(shù)據(jù)時(shí),即可以通過:

            Any myAnyVal(25);

            int nIntData = myAnyVal;

            這種形式得到想要的值。但是需要注意的是,這里不可以對Any的隱式轉(zhuǎn)換做正確性的假定。即,我們不能寫下如下代碼:

            Any myAnyVal(25);

            float fData = myAnyVal;

            我們不能指望這里返回正確的值。因?yàn)锳ny中保存的數(shù)據(jù)實(shí)際為int,而當(dāng)這個(gè)賦值發(fā)生時(shí),根據(jù)重載決議,編譯器會調(diào)用Any的operator float()返回一個(gè)float值。而由于float在內(nèi)存中的放置是基于IEEE浮點(diǎn)格式的,int則是2進(jìn)制的數(shù)據(jù),最后返回的數(shù)據(jù)就不可能正確了。

            基于此,我們要求在使用Any的時(shí)候,存放數(shù)據(jù)的位置和取出數(shù)據(jù)的位置,都必須由程序員指定對應(yīng)好的數(shù)據(jù)類型,并且寄希望于程序員知道自己在做什么。

             

            上述以上的實(shí)現(xiàn)方法有2個(gè)好處:

            1、  節(jié)省內(nèi)存,對于不同大小的數(shù)據(jù)類型,通過union的形式共用了存儲空間。

            2、  速度快,沒有使用動態(tài)分配內(nèi)存的模式,而是直接將對象放置在union空間中。

             

            但是如果我們想讓這個(gè)Any支持std::string,就不能像上述實(shí)現(xiàn)得那么直接了。

             

            因?yàn)閡nion中不能支持non-trivial的數(shù)據(jù)類型(因?yàn)閷τ趎on-trivial的數(shù)據(jù)類型,編譯器在為其分配內(nèi)存空間之后,還要調(diào)用其構(gòu)造函數(shù))。我們必須另外想一種方法。

            一種簡單的容易想到的方法是保存指針,如下:

             

            union {

                     int nIntData;

            std::string * ptrStr;

            };

             

            這樣,我們需要在operator =以及copy constructor中手動管理內(nèi)存。當(dāng)Any初始化為String類型時(shí),需要?jiǎng)討B(tài)申請內(nèi)存,而如果構(gòu)建好的Any中所含的類型在operator =中由String轉(zhuǎn)為其他類型時(shí),需要將其動態(tài)釋放,在由其他類型轉(zhuǎn)為String時(shí),則需要?jiǎng)討B(tài)申請。如果這里的其他類型是另外的動態(tài)類型,且也是通過指針保存在union結(jié)構(gòu)中的,則也需要做相應(yīng)的釋放&申請?zhí)幚怼?/p>

             

            如下:

            Any& operator = (int inData)

            {

                     switch (m_type)

                     {

                     case ANYTYPE_STRING:

                     delete ptrStr;

            nIntData = inData;

                               break;

                     // other cases

            };

            m_type = ANYTYPE_INT;

            }

             

            Any& operator = (std::string& inData)

            {

                     switch (m_type)

                     {

                     case ANYTYPE_INT:

                              ptrStr = new std::string(inData);

                               break;

                     // other cases

            };

            m_type = ANYTYPE_STRING;

            }

             

            除了以上所述的operator = 中所增加的代碼之外,constructor以及copy constructor針對std::string重載的版本也需要做內(nèi)存分配。destructor中也需要根據(jù)數(shù)據(jù)的實(shí)際類型判斷是否釋放相應(yīng)的指針。

             

            現(xiàn)在,這種做法對于C++內(nèi)置類型像之前一樣是直接支持的。但對于用戶自定義類型,則需要通過指針的形式,動態(tài)的創(chuàng)建以及釋放。當(dāng)Any類中包含的數(shù)據(jù)從基本類型切換到non-trivial類型或者反向切換的時(shí)候,需要釋放或者分配內(nèi)存;但是在基本類型之間切換的時(shí)候,不需要做動態(tài)內(nèi)存分配&釋放。

            這種不一致性導(dǎo)致了代碼需要根據(jù)類型不同做出不同的處理,隨著Any支持類型的增多,不可避免的代碼膨脹發(fā)生了,而且每增加一種新類型,需要修改所有重載版本的operator =,以及新增一份constructor及copy constructor,并在destructor中增加對應(yīng)類型的判斷。

            最為讓人惱火的是,Any在處理trivial類型和non-trivial類型時(shí)的行為不一。這非常容易導(dǎo)致錯(cuò)誤。

             

            下面,我們嘗試把問題簡化一下,通過讓Any始終保存指針來避免行為不統(tǒng)一的問題。無論從何種類型切換到另一種類型,我們都確保必須釋放先前的內(nèi)存,并為目標(biāo)類型分配新的內(nèi)存。

             

            新的數(shù)據(jù)結(jié)構(gòu)可以設(shè)置如下:

            class Any {

            public:

                     void * m_pointer;

                     int m_nType;

             

            // supporting types

            enum {

            ANYTYPE_INT,

            ANYTYPE_FLOAT,

            ANYTYPE_STRING,

            //… other supporting types

            };

             

            Any(int inData) : m_nType(ANYTYPE_INT), m_pointer(new int(inData)) {}

            Any(float inData) : m_nType(ANYTYPE_FLOAT), m_pointer(new float(inData) {}

            Any(const std::string& inData) :

            m_nType(ANYTYPE_STRING), m_pointer(new std::string(inData)) {}

            Any(const Any& rhs) : m_nType(rhs.m_nType) {

                     switch (m_nType)

                     {

                     case ANYTYPE_INT:

                               m_pointer = new int(*((int *)rhs.m_pointer));

                               break;

                     case ANYTYPE_FLOAT:

                               m_pointer = new float(*((float *)rhs.m_pointer));

                               break;

                     case ANYTYPE_STRING:

                               m_pointer = new std::string(*((std::string *)rhs.m_pointer));

                               break;

            }

            }

            Any& Operator = (int inData) {

            if (m_nType == ANYTYPE_INT)

                     *(int*)m_pointer = inData;

            else {

                     switch (m_nType)

                     {

                     case ANYTYPE_FLOAT:

                               delete (float*)m_pointer;

                               break;

                     case ANYTYPE_STRING:

                               delete (std::string*)m_pointer;

                               break;

            }

            m_pointer = new int(inData);

            };

            }

            // other operator equals…

            };

             

            現(xiàn)在的情況比之前好很多,我們不用再為Any在對待trivial類型數(shù)據(jù)與non-trivial數(shù)據(jù)時(shí)的行為不一而頭痛了。

            但是問題依舊很麻煩,因?yàn)槭褂胿oid指針消除了存儲時(shí)的類型信息,所以當(dāng)delete指針時(shí),我們需要人為的指定每一處指針?biāo)复念愋停瑥亩咕幾g器得以調(diào)用正確的析構(gòu)函數(shù)。

            從而,每一個(gè)重載版本的operator =當(dāng)中,我們都需要判斷當(dāng)前的類型是否與傳入?yún)?shù)的類型相符,若不相符,需要根據(jù)存儲的類型標(biāo)識符m_nType對m_pointer轉(zhuǎn)型,并使用delete operator完成內(nèi)存釋放的工作,而后再為傳入?yún)?shù)分配新的內(nèi)存。

             

            來回轉(zhuǎn)型可能令你覺得厭煩。或許你會想到將不同種類的指針放在union當(dāng)中,這樣就不必為void指針轉(zhuǎn)型了。但這樣做行不通,原因是我們依舊需要根據(jù)m_nType的類型決定使用union中的哪個(gè)成員,實(shí)際上依舊等價(jià)于上面的做法,只不過省卻了轉(zhuǎn)型操作符(將轉(zhuǎn)型操作符的工作移交到union的定義當(dāng)中了)。

             

            難道沒有更好的方法嗎?

            在思考如何實(shí)現(xiàn)Any的過程中,我們發(fā)現(xiàn)了它的兩個(gè)特點(diǎn),譬如:

            1、  我們需要保存類型信息:m_nType以及相應(yīng)的enum定義;

            2、  在使用Any時(shí),我們需要明確的指出其contain的數(shù)據(jù)類型,以此得到正確的數(shù)據(jù)。(例如,使用int構(gòu)建的Any,從Any中把數(shù)據(jù)拿出來時(shí),目標(biāo)也應(yīng)當(dāng)是一個(gè)int,而不能是float,否則會調(diào)用到錯(cuò)誤的重載函數(shù),從而得出錯(cuò)誤結(jié)果)

            3、  我們需要針對Any支持的所有類型實(shí)現(xiàn)constructor & operator = & operator T的重載版本。

             

            為什么不利用C++自身的設(shè)施完成這種工作呢?

            由第1、2點(diǎn),我們發(fā)現(xiàn),Any在存數(shù)和取數(shù)的時(shí)候,需要使用對應(yīng)的數(shù)據(jù)類型,從而調(diào)用匹配正確的構(gòu)造函數(shù)/operator =以及operator T() (這里的T指代各種類型如int, float)的重載版本。而且,Any被賦予一個(gè)值之后,再未被再次賦予其他數(shù)據(jù)類型的值之前,類型信息是始終保存在Any當(dāng)中的。而當(dāng)Any保存的數(shù)據(jù)類型變動時(shí),對應(yīng)的類型ID也需要更新。在這里,C++的運(yùn)行時(shí)類型信息(runtime type info)正是用武之地。

             

            在實(shí)現(xiàn)針對不同類型的重載函數(shù)時(shí),我們發(fā)現(xiàn)幾乎絕大多數(shù)工作都是重復(fù)或者類似的,在早期的版本中,甚至可以用宏來節(jié)省代碼編寫的工作量。C++中的模板正暗合了這里的需求。

             

            下面,我們開始隨著Ogre::Any的設(shè)計(jì)思路前行。(Ogre::Any使用了Boost::Any,但在數(shù)值A(chǔ)ny以及stream操作上做了擴(kuò)展)

             

            如果利用模板,我們就不必再為每一種需要支持的類型寫一個(gè)重載版本的constructor & operator = & operator T了。

            C++的成員函數(shù)模板使我們可以寫下類似下面這樣的代碼:

            class Any {

            public:

            template <typename ValueType>

            Any(const ValueType& v);

             

            template <typename ValueType>

            Any& operator = (const ValueType& v);

             

            template <typename ValueType>            

            operator ValueType() const;                       // 實(shí)際實(shí)現(xiàn)中并沒有定義這個(gè)

            };

             

            但是operator ValueType()的約束太過于寬泛了,有了它的存在,現(xiàn)在Any可能被用在任何我們意想不到的地方。所以實(shí)際的實(shí)現(xiàn)中Ogre::Any并沒有定義轉(zhuǎn)型操作符,而是使用了名為any_cast的函數(shù),當(dāng)我們需要在某個(gè)地方從Any中取出我們想要的數(shù)據(jù)時(shí),我們必須清楚這個(gè)Any里放著的是什么,并且明確的把它c(diǎn)ast出來。

            any_cast的聲明如下:

            template <typename ValueType>

            ValueType* any_cast(Any* anyVal);

             

            template <typename ValueType>

            const ValueType* any_cast(const Any* anyVal);

             

            template <typename ValueType>

            ValueType any_cast(const Any& anyVal);

             

            這樣我們就可以用像使用static_cast一樣的方法,使用any_cast,如下:

            Any anyVal(3246);

            int nVal = any_cast<int>(anyVal);

             

            OK,模板現(xiàn)在節(jié)約了我們大量的重復(fù)勞動,一個(gè)模板就涵蓋了所有的可能,我們不必再為以后需要新增加的數(shù)據(jù)類型而頭痛了。

            基于成員函數(shù)模板的Any看起來似乎不錯(cuò)。但是究竟應(yīng)該如何存儲數(shù)據(jù)呢?

             

            首先,想到的是在Any中置放一個(gè)模板成員變量,像這樣:

            class Any {

            public:

            template <typename ValueType>

            Any(const ValueType& v);

            private:

            T m_val;

            };

             

            但是這樣做顯然行不通,因?yàn)檫@樣一來Any類就必須是一個(gè)模板類了。這不符合我們對Any的期望。況且,一旦確定了Any的模板參數(shù),他也就成了一個(gè)只能承載確定類型的wrapper了。這不是我們想要的。

             

            那么如果不保存模板成員,而是使用模板成員指針是否可行呢?答案依舊是不可行。因?yàn)樾稳鏣* m_val;的定義依舊需要在編譯期獲知T的準(zhǔn)確類型。一旦在編譯期確定了準(zhǔn)確的類型,我們就無法在運(yùn)行期動態(tài)改變他了。

            我們需要的是一個(gè)運(yùn)行期可以動態(tài)變化的模板成員。

             

            由于前面的分析,直接將一個(gè)模板成員存儲于Any當(dāng)中是行不通的,但是我們卻可以保存一個(gè)確定類型的指針,并讓這個(gè)指針應(yīng)該指向?qū)嶋H存儲我們需要的模板數(shù)據(jù)的實(shí)例。

             

            通過類似于以下這樣的繼承關(guān)系:

            class placeholder;

            class holder<T> : public placeholder;

            我們擁有了承載無窮種數(shù)據(jù)類型的可能性。

            該繼承關(guān)系如下圖:

             

            有了這樣的數(shù)據(jù)承載類之后,Any中只需保存一個(gè)placeholder接口的指針即可。而在constructor / operator = 的時(shí)候,只需刪除此前的數(shù)據(jù),并為新的數(shù)據(jù)類型創(chuàng)建對應(yīng)的holder<T>實(shí)例即可。

             

            placeholder需要定義clone接口,用于在copy constructor中生成數(shù)據(jù)的拷貝。

            需要定義getType接口,用于在any_cast中識別當(dāng)前保存的數(shù)據(jù)類型與目標(biāo)類型是否一致。(getType的實(shí)現(xiàn)可以采用此前的enum + m_nType的方法,但是這種方法的局限性在于我們需要為每一個(gè)可能的類型增加一個(gè)標(biāo)識符,因此更好的做法是使用C++的運(yùn)行時(shí)類型識別信息RTTI:Runtime type info / Runtime type identify)

             

            實(shí)際的placeholder定義如下:

            class placeholder {

            public:

            virtual ~placeholder() {}                               // 虛析構(gòu)函數(shù)用以保證派生類的析構(gòu)函數(shù)得以調(diào)用

            virtual placeholder * clone() const = 0;  

            virtual const std::type_info& getType() const = 0; // 返回rtti信息

            virtual void writeToStream(std::ostream& o) = 0;  // 串行化支持

            };

             

            真正的承載數(shù)據(jù)的類模板holder定義很簡單,實(shí)現(xiàn)相應(yīng)的基類接口即可,如下:

            template<typename ValueType>

            class holder : public placeholder

            {

            public: // structors

             

            holder(const ValueType & value) : held(value) { }

            virtual const std::type_info & getType() const { return typeid(ValueType); }

                virtual placeholder * clone() const { return new holder(held); }

                     virtual void writeToStream(std::ostream& o) { o << held; }

             

            ValueType held;

            };

             

            這樣一來,Any的定義就自然而生了:

            class Any {

            public:

              Any() : mContent(0) {}

            template<typename ValueType>

            explicit Any(const ValueType & value)

            : mContent(new holder<ValueType>(value)) { }

            Any(const Any & other)

            : mContent(other.mContent ? other.mContent->clone() : 0) { }

            virtual ~Any() { delete mContent; }

             

            Any& swap(Any & rhs)

              {

                   std::swap(mContent, rhs.mContent);

                return *this;

              }

            template<typename ValueType>

              Any& operator=(const ValueType & rhs)

              {

                   Any(rhs).swap(*this);

                return *this;

            }

            Any & operator=(const Any & rhs)

              {

            Any(rhs).swap(*this);

                return *this;

            }

             

            bool isEmpty() const { return !mContent; }

              const std::type_info& getType() const

              { return mContent ? mContent->getType() : typeid(void); }

             

            inline friend std::ostream& operator <<

            ( std::ostream& o, const Any& v )

            {

            if (v.mContent) v.mContent->writeToStream(o);

                     return o;

            }

            protected:

            placeholder* mContent;

            template<typename ValueType>

                friend ValueType * any_cast(Any *);

            };

             

            以上就是Any的幾乎所有的定義了。此前介紹的holder以及placeholder由于只在Any中被使用到,因此在實(shí)做中被定義為Any的嵌套類。

             

            any_cast的定義具體如下:

            template<typename ValueType>

            ValueType * any_cast(Any * operand)

            {

            return operand && operand->getType() == typeid(ValueType)

                         ? &static_cast<Any::holder<ValueType> *>(operand->mContent)->held

                    : 0;

            }

            需要判斷被cast的Any中所貯存的數(shù)據(jù)的類型是否與目標(biāo)類型一致,判斷采用了C++的RTTI中的typeid。當(dāng)類型不一致時(shí),返回的結(jié)果為0。這一點(diǎn)的行為與dynamic_cast類似(dynamic_cast在正常的轉(zhuǎn)型失敗時(shí)會返回0)。使用any_cast而不是用類型轉(zhuǎn)換操作符的原因,一方面在于基于模板的類型轉(zhuǎn)換操作符過于隨意,另一方面在于any_cast的使用方式與static_cast等幾乎完全一致,符合C++的使用習(xí)慣,且當(dāng)需要查找程序中有多少地方使用了any_cast時(shí),一個(gè)grep就可以簡單的給出結(jié)果。

             

            另外兩個(gè)重載版本的any_cast皆是調(diào)用上述指針版本的any_cast完成的。

             

            至此,關(guān)于Any的實(shí)現(xiàn)的探討告一段落。

            最終Ogre::Any利用C++的模板特性,RTTI特性實(shí)現(xiàn)了一個(gè)非常具有實(shí)用價(jià)值的任意類型構(gòu)件。由于采用了指針存儲數(shù)據(jù),如果大量的使用Any,會比基于union以及基本類型的實(shí)現(xiàn)方式速度緩慢一些。

            然而對于期待高速度的多類型組合結(jié)構(gòu),在Boost中存在另一個(gè)構(gòu)件可以達(dá)到相應(yīng)目的,即Boost::Variant。

            Boost::Variant可以像這樣使用:Boost::Variant<int, float, std::string, vector<int> > myVariant;

            具體Boost::Variant的使用以及實(shí)現(xiàn)方式,不在本文的探討范圍內(nèi)。如果對此感興趣,可以參閱Boost的文檔:http://www.boost.org/doc/libs/1_37_0/doc/html/variant.html

             

            另外,關(guān)于Boost::Any的實(shí)現(xiàn),劉未鵬還撰寫過一篇非常優(yōu)秀的文章:

            http://blog.csdn.net/pongba/archive/2004/08/24/82811.aspx

             

             

             

             

            附:Ogre中除了使用Boost::Any之外,還對Any在數(shù)值上的應(yīng)用做了擴(kuò)展:

            Ogre在Any的基礎(chǔ)上,實(shí)現(xiàn)了+-*/的數(shù)學(xué)運(yùn)算操作符,構(gòu)成了AnyNumberic類。

            Any::placeholder接口實(shí)現(xiàn)了getType,clone,writeToStream以實(shí)現(xiàn)對于實(shí)際數(shù)據(jù)的取類型,拷貝,寫流操作。

            在AnyNumberic中,numplaceholder的實(shí)現(xiàn)則需附加定義一套加減乘除的操作接口,從而使得AnyNumberic可以實(shí)現(xiàn)+-*/等運(yùn)算符。(Any的結(jié)構(gòu)此前已經(jīng)分析過了,通過調(diào)用placeholder的接口實(shí)現(xiàn)對實(shí)際數(shù)據(jù)的操作,而placeholder接口背后的實(shí)現(xiàn),則是一個(gè)根據(jù)初始化Any的實(shí)際類型實(shí)例化的模板類,在AnyNumberic中,這一結(jié)構(gòu)相同)

            class numplaceholder : public Any::placeholder {

                     virtual ~numplaceholder() {} // override virtual destructor

                     virtual placeholder add(placeholder* rhs) = 0;

                     virtual placeholder subtract(placeholder* rhs) = 0;

                     virtual placeholder multiply(placeholder* rhs) = 0;

                     virtual placeholder multiply(float rhs) = 0; // override version

                     virtual placeholder divide(placeholder* rhs) = 0;

            };

             

            template<typename ValueType>

            class numholder : public numplaceholder {

            //…

            };

            利用這一實(shí)現(xiàn),AnyNumberic即可采用如下方式實(shí)現(xiàn)operator運(yùn)算符:

            AnyNumeric AnyNumeric::operator+(const AnyNumeric& rhs) const

            {

                     return AnyNumeric(

                                                 static_cast<numplaceholder*>(mContent)->add(rhs.mContent));

            }

            其中mContent為繼承自Any的數(shù)據(jù)成員,類型為Any::placeholder的指針。

            AnyNumberic沒有定義新的數(shù)據(jù)成員,僅僅是提供了一些新的接口,并通過派生Any::placeholder在不改動舊有功能的基礎(chǔ)上,提供了新的數(shù)學(xué)運(yùn)算的能力。



            posted on 2009-06-01 16:16 李陽 閱讀(1344) 評論(0)  編輯 收藏 引用

            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            丰满少妇人妻久久久久久4| 无码专区久久综合久中文字幕| 久久777国产线看观看精品| 99精品久久精品一区二区| 久久久综合九色合综国产| 亚洲精品NV久久久久久久久久| 777午夜精品久久av蜜臀| 99久久99久久久精品齐齐| 很黄很污的网站久久mimi色| 久久无码中文字幕东京热| 成人久久综合网| 国产精品久久婷婷六月丁香| 色噜噜狠狠先锋影音久久| 国产成人精品综合久久久久| 丰满少妇人妻久久久久久4| 婷婷久久久亚洲欧洲日产国码AV| 国产精品嫩草影院久久| 狠狠色丁香婷综合久久| 久久偷看各类wc女厕嘘嘘| 久久综合久久综合亚洲| 久久久99精品一区二区| 久久这里只精品国产99热| 国内精品久久久久久久97牛牛 | 日韩精品久久久久久久电影蜜臀 | 狠狠色丁香久久综合五月| 亚洲日本久久久午夜精品| 精品久久久久久久中文字幕| 欧美精品一本久久男人的天堂| 国产午夜福利精品久久2021| 久久久久久久精品妇女99| 精品久久久久成人码免费动漫| 国内精品伊人久久久久影院对白 | 精品久久人人爽天天玩人人妻| 99久久这里只有精品| 久久婷婷五月综合色高清| 伊人久久大香线焦AV综合影院| 香蕉久久夜色精品国产2020| 亚洲伊人久久综合影院| 久久久久久久91精品免费观看| 久久综合亚洲色一区二区三区| 狠狠色丁香久久婷婷综合_中|