欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++運行時類型識別與轉(zhuǎn)換實現(xiàn)方法

 更新時間:2022年10月09日 08:43:20   作者:哎呀熊熊熊  
運行時類型識別可能被認為是C++中一個”次要“的特征,當程序員在編程過程中陷入非常困難的境地時,實用主義將會幫助他走出困境

當僅有一個指針或引用指向基類型時,利用運行時類型識別(RTTI)可以找到一個對象的動態(tài)類型。

運行時類型識別可能被認為是C++中一個”次要“的特征,當程序員在編程過程中陷入非常困難的境地時,實用主義將會幫助他走出困境。正常情況下,程序員需要有意忽略對象的準確類型,而利用虛函數(shù)機制實現(xiàn)那個類型正確操作過程。然而,有時知道一個僅含一個基類指針的對象的準確的運行時類型(即多半是派生的類型)是非常有用的。有了此信息,就可以更有效地進行某些特殊情況的操作,或者預(yù)防基類接口因無此信息而變得笨拙。

1.運行時類型轉(zhuǎn)換

通過指針或者引用決定對象運行時類型的一種方法是使用運行時類型轉(zhuǎn)換(runtime cast),用這種方法可以查證所嘗試進行的轉(zhuǎn)換正確與否。當要把基類指針類型轉(zhuǎn)換為派生類時,這個方法非常有用。由于繼承的層次結(jié)構(gòu)的典型描述說基類在派生類之上,所以這種類型轉(zhuǎn)換也成為向下類型轉(zhuǎn)換(downcast)。

請看下面的類層次結(jié)構(gòu):

在下面的程序代碼中,Investment類中有一個其他類沒有的額外操作,所以能夠運行時知道Security指針是否引用了Investment對象是很重要的。為了實現(xiàn)檢查運行時的類型轉(zhuǎn)換,每個類都持有一個整數(shù)標識符,以便可以與層次結(jié)構(gòu)中其他的類區(qū)別開來。

#include <iostream>
#include <vector>
#include "../purge.h"
using namespace std;
class Security {
protected:
  enum { BASEID = 0 };
public:
  virtual ~Security() {}
  virtual bool isA(int id) { return (id == BASEID); }
};
class Stock : public Security {
  typedef Security Super;
protected:
  enum { OFFSET = 1, TYPEID = BASEID + OFFSET };
public:
  bool isA(int id) {
    return id == TYPEID || Super::isA(id);
  }
  static Stock* dynacast(Security* s) {
    return (s->isA(TYPEID)) ? static_cast<Stock*>(s) : 0;
  }
};
class Bond : public Security {
  typedef Security Super;
protected:
  enum { OFFSET = 2, TYPEID = BASEID + OFFSET };
public:
  bool isA(int id) {
    return id == TYPEID || Super::isA(id);
  }
  static Bond* dynacast(Security* s) {
    return (s->isA(TYPEID)) ? static_cast<Bond*>(s) : 0;
  }
};
class Investment : public Security {
  typedef Security Super;
protected:
  enum { OFFSET = 3, TYPEID = BASEID + OFFSET };
public:
  bool isA(int id) {
    return id == TYPEID || Super::isA(id);
  }
  static Investment* dynacast(Security* s) {
    return (s->isA(TYPEID)) ?
      static_cast<Investment*>(s) : 0;
  }
  void special() {
    cout << "special Investment function" << endl;
  }
};
class Metal : public Investment {
  typedef Investment Super;
protected:
  enum { OFFSET = 4, TYPEID = BASEID + OFFSET };
public:
  bool isA(int id) {
    return id == TYPEID || Super::isA(id);
  }
  static Metal* dynacast(Security* s) {
    return (s->isA(TYPEID)) ? static_cast<Metal*>(s) : 0;
  }
};
int main() {
  vector<Security*> portfolio;
  portfolio.push_back(new Metal);
  portfolio.push_back(new Investment);
  portfolio.push_back(new Bond);
  portfolio.push_back(new Stock);
  for(vector<Security*>::iterator it = portfolio.begin();
       it != portfolio.end(); ++it) {
    Investment* cm = Investment::dynacast(*it);
    if(cm)
      cm->special();
    else
      cout << "not an Investment" << endl;
  }
  cout << "cast from intermediate pointer:" << endl;
  Security* sp = new Metal;
  Investment* cp = Investment::dynacast(sp);
  if(cp) cout << "  it's an Investment" << endl;
  Metal* mp = Metal::dynacast(sp);
  if(mp) cout << "  it's a Metal too!" << endl;
  purge(portfolio);
} ///:~

多態(tài)的isA()函數(shù)檢查其參數(shù)是否與它的類型參數(shù)(id)相容,就意味著或者id與對象的typeID準確地匹配,或者與對象的祖先之一的類型匹配(因為在這種情況下調(diào)用Super::isA())。函數(shù)dynacast()在每個類中都是靜態(tài)的,dynacast()為其指針參數(shù)調(diào)用isA()來檢查類型轉(zhuǎn)換是否有效。

借助dynamic_cast操作符,C++提供這樣一個可檢查的類型轉(zhuǎn)換。使用dynamic_cast對前面的程序例子進行重寫,就得到下面的程序:

//: C08:Security.h
#ifndef SECURITY_H
#define SECURITY_H
#include <iostream>
class Security {
public:
  virtual ~Security() {}
};
class Stock : public Security {};
class Bond : public Security {};
class Investment : public Security {
public:
  void special() {
    std::cout << "special Investment function" <<std::endl;
  }
};
class Metal : public Investment {};
#endif // SECURITY_H ///:~
// Uses RTTI's dynamic_cast.
#include <vector>
#include "../purge.h"
#include "Security.h"
using namespace std;
int main() {
  vector<Security*> portfolio;
  portfolio.push_back(new Metal);
  portfolio.push_back(new Investment);
  portfolio.push_back(new Bond);
  portfolio.push_back(new Stock);
  for(vector<Security*>::iterator it =
       portfolio.begin();
       it != portfolio.end(); ++it) {
    Investment* cm = dynamic_cast<Investment*>(*it);
    if(cm)
      cm->special();
    else
      cout << "not a Investment" << endl;
  }
  cout << "cast from intermediate pointer:" << endl;
  Security* sp = new Metal;
  Investment* cp = dynamic_cast<Investment*>(sp);
  if(cp) cout << "  it's an Investment" << endl;
  Metal* mp = dynamic_cast<Metal*>(sp);
  if(mp) cout << "  it's a Metal too!" << endl;
  purge(portfolio);
} ///:~

由于原來例子中大部分的代碼開銷用在了類型轉(zhuǎn)換檢查上,所以這個例子就變得如此之短。如果想要安全地進行向下類型轉(zhuǎn)換,dynamic_cast要求使用的目標類型是多態(tài)的(polymorphic)。這就要求該類必須至少有一個虛函數(shù)。幸運的是,Security基類有一個虛析構(gòu)函數(shù),所以這里不需要再創(chuàng)建一個額外的函數(shù)去做這項工作。因為dynamic_cast在程序運行時使用了虛函數(shù)表,所以比其他新式風格的類型轉(zhuǎn)換操作來說它的代價更高。

用引用而非指針同樣也可以使用dynamic_cast,但是由于沒有諸如空引用這樣的情況,這就需要采用其他方法來了解類型轉(zhuǎn)換是否失敗。這個”其他方法“就是捕獲bad_cast異常,如下所示:

#include <typeinfo>
#include "Security.h"
using namespace std;
int main() {
  Metal m;
  Security& s = m;
  try {
    Investment& c = dynamic_cast<Investment&>(s);
    cout << "It's an Investment" << endl;
  } catch(bad_cast&) {
    cout << "s is not an Investment type" << endl;
  }
  try {
    Bond& b = dynamic_cast<Bond&>(s);
    cout << "It's a Bond" << endl;
  } catch(bad_cast&) {
    cout << "It's not a Bond type" << endl;
  }
} ///:~

2.typeid操作符

獲得有關(guān)一個對象運行時信息的另一個方法,就是typeid操作符來完成。這種操作符返回一個type_info類的對象,該對象給出與其應(yīng)用有關(guān)的對象類型的信息。如果該對象的類型是多態(tài)的,它將給出那個應(yīng)用(動態(tài)類型(dynamic type))的大部分派生類信息;否則,它將給出靜態(tài)類型信息。typeid操作符的一個用途是獲得一個對象的動態(tài)類型的名稱,例如const char * ,就像在下面例子中可以看到的:

#include <iostream>
#include <typeinfo>
using namespace std;
struct PolyBase { virtual ~PolyBase() {} };
struct PolyDer : PolyBase { PolyDer() {} };
struct NonPolyBase {};
struct NonPolyDer : NonPolyBase { NonPolyDer(int) {} };
int main() {
  // Test polymorphic Types
  const PolyDer pd;
  const PolyBase* ppb = &pd;
  cout << typeid(ppb).name() << endl;
  cout << typeid(*ppb).name() << endl;
  cout << boolalpha << (typeid(*ppb) == typeid(pd))
       << endl;
  cout << (typeid(PolyDer) == typeid(const PolyDer))
       << endl;
  // Test non-polymorphic Types
  const NonPolyDer npd(1);
  const NonPolyBase* nppb = &npd;
  cout << typeid(nppb).name() << endl;
  cout << typeid(*nppb).name() << endl;
  cout << (typeid(*nppb) == typeid(npd)) << endl;
  // Test a built-in type
  int i;
  cout << typeid(i).name() << endl;
} ///:~

這是使用一個特定編譯器的程序的輸出是:

struct PolyBase const *
struct Polyder
true
true
struct NonPolyBase const *
struct NonPolyBase
false
int

因為ppb是一個指針,所以輸出的第1行是他的靜態(tài)類型。為了在程序中得到RTTI的結(jié)果,需要檢查指針或引用目標對象,這在第2行說明。需要注意的是,RTTI忽略了頂層的const和volatile限定符。借助非多態(tài)類型,正好可以獲得靜態(tài)類型(該指針本身的類型)。正如讀者所見,這里也支持內(nèi)置類型。

2.1類型轉(zhuǎn)換到中間層次類型

#include <cassert>
#include <typeinfo>
using namespace std;
class B1 {
public:
  virtual ~B1() {}
};
class B2 {
public:
  virtual ~B2() {}
};
class MI : public B1, public B2 {};
class Mi2 : public MI {};
int main() {
  B2* b2 = new Mi2;
  Mi2* mi2 = dynamic_cast<Mi2*>(b2);
  MI* mi = dynamic_cast<MI*>(b2);
  B1* b1 = dynamic_cast<B1*>(b2);
  assert(typeid(b2) != typeid(Mi2*));
  assert(typeid(b2) == typeid(B2*));
  delete b2;
} ///:~

如果創(chuàng)建一個Mi2對象并將它向上類型轉(zhuǎn)換到該繼承層次結(jié)構(gòu)的根(在這種情況下,選擇兩個可能的根中的一個),可以成功地使dynamic_cast回退至兩個派生層MI或Mi2中的任何一個。

甚至可以從一個根到另一個根進行類型轉(zhuǎn)換:

B1* b1 = dynamic_cast<B1*>(b2);

這也是成功的,因為B2實際上指向一個Mi2對象,該Mi2對象含有一個B1類型的子對象。

2.2void型指針

RTTI僅僅為完整的類型工作,這就意味著當使用typeid時,所有的類型信息必須是可利用的。特別是,它不能與void型指針一起工作:

//!#include <iostream>
#include <typeinfo>
using namespace std;
class Stimpy {
public:
  virtual void happy() {}
  virtual void joy() {}
  virtual ~Stimpy() {}
};
int main() {
  void* v = new Stimpy;
  // Error:
//!  Stimpy* s = dynamic_cast<Stimpy*>(v);
  // Error:
//!  cout << typeid(*v).name() << endl;
} ///:~

一個void* 真是的意思是”無類型信息“。

2.3運用帶模板的RTTI

因為所有的類模板所做的工作就是產(chǎn)生類,所以類模板可以很好地與RTTI一起工作。RTTI提供了一條方便的途徑來獲得對象所在類的名稱。下面的示例打印出構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用順序:

#include <iostream>
#include <typeinfo>
using namespace std;
template<int id> class Announce {
public:
  Announce() {
    cout << typeid(*this).name() << " constructor" << endl;
  }
  ~Announce() {
    cout << typeid(*this).name() << " destructor" << endl;
  }
};
class X : public Announce<0> {
  Announce<1> m1;
  Announce<2> m2;
public:
  X() { cout << "X::X()" << endl; }
  ~X() { cout << "X::~X()" << endl; }
};
int main() { X x; } ///:~

在構(gòu)造函數(shù)內(nèi)和析構(gòu)函數(shù)內(nèi)部,RTTI信息產(chǎn)生打印的類名。類X利用繼承和組合兩個方式創(chuàng)建一個類。輸出如下:

Announce<0> constructor
Announce<1> constructor
Announce<2> constructor
X::X()
X::~X()
Announce<2> destructor
Announce<1> destructor
Announce<0> destructor

當然,可能會得到不同的結(jié)果,這取決于編譯器如何表示它的name()信息。

3.多重繼承

RTTI機制必須正確地處理多重繼承的所有復雜性,包括虛基類virtual:

#include <iostream>
#include <typeinfo>
using namespace std;
class BB {
public:
  virtual void f() {}
  virtual ~BB() {}
};
class B1 : virtual public BB {};
class B2 : virtual public BB {};
class MI : public B1, public B2 {};
int main() {
  BB* bbp = new MI; // Upcast
  // Proper name detection:
  cout << typeid(*bbp).name() << endl;
  // Dynamic_cast works properly:
  MI* mip = dynamic_cast<MI*>(bbp);
  // Can't force old-style cast:
//! MI* mip2 = (MI*)bbp; // Compile error
} ///:~

typeid()操作符正確地檢測出實際對象的名字,即便它采用virtual基類指針完成這個任務(wù)的,dynamic_cast也正確地進行工作。但實際上,編譯器不允許程序員用以前的方法嘗試強制進行類型轉(zhuǎn)換:

MI* mip2 = (MI*)bbp; // Compile error

編譯器知道這樣做絕不是正確的方法,因此需要程序員使用dynamic_cast。

4.合理使用RTTI

垃圾再生器

為了進一步地舉例說明RTTI的實際用途,下面的程序模擬了一個垃圾再生器。不同種類的”垃圾“被插入一個容器中,然后根據(jù)它們的動態(tài)類型進行分類。

// Describing trash.
#ifndef TRASH_H
#define TRASH_H
#include <iostream>
class Trash {
  float _weight;
public:
  Trash(float wt) : _weight(wt) {}
  virtual float value() const = 0;
  float weight() const { return _weight; }
  virtual ~Trash() {
    std::cout << "~Trash()" << std::endl;
  }
};
class Aluminum : public Trash {
  static float val;
public:
  Aluminum(float wt) : Trash(wt) {}
  float value() const { return val; }
  static void value(float newval) {
    val = newval;
  }
};
class Paper : public Trash {
  static float val;
public:
  Paper(float wt) : Trash(wt) {}
  float value() const { return val; }
  static void value(float newval) {
    val = newval;
  }
};
class Glass : public Trash {
  static float val;
public:
  Glass(float wt) : Trash(wt) {}
  float value() const { return val; }
  static void value(float newval) {
    val = newval;
  }
};
#endif // TRASH_H ///:~

用來表示垃圾類型單價的static值定義在實現(xiàn)文件中:

// A Trash Recycler.
#include "Trash.h"
float Aluminum::val = 1.67;
float Paper::val = 0.10;
float Glass::val = 0.23;
///:~

sunValue()模板從頭到尾對一個容器進行迭代,顯示并計算結(jié)果:

//{L} Trash
// A Trash Recycler.
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <typeinfo>
#include <vector>
#include "Trash.h"
#include "../purge.h"
using namespace std;
// Sums up the value of the Trash in a bin:
template<class Container>
void sumValue(Container& bin, ostream& os) {
  typename Container::iterator tally = bin.begin();
  float val = 0;
  while(tally != bin.end()) {
    val += (*tally)->weight() * (*tally)->value();
    os << "weight of " << typeid(**tally).name()
       << " = " << (*tally)->weight() << endl;
    ++tally;
  }
  os << "Total value = " << val << endl;
}
int main() {
  srand(time(0)); // Seed the random number generator
  vector<Trash*> bin;
  // Fill up the Trash bin:
  for(int i = 0; i < 30; i++)
    switch(rand() % 3) {
      case 0 :
        bin.push_back(new Aluminum((rand() % 1000)/10.0));
        break;
      case 1 :
        bin.push_back(new Paper((rand() % 1000)/10.0));
        break;
      case 2 :
        bin.push_back(new Glass((rand() % 1000)/10.0));
        break;
    }
  // Note: bins hold exact type of object, not base type:
  vector<Glass*> glassBin;
  vector<Paper*> paperBin;
  vector<Aluminum*> alumBin;
  vector<Trash*>::iterator sorter = bin.begin();
  // Sort the Trash:
  while(sorter != bin.end()) {
    Aluminum* ap = dynamic_cast<Aluminum*>(*sorter);
    Paper* pp = dynamic_cast<Paper*>(*sorter);
    Glass* gp = dynamic_cast<Glass*>(*sorter);
    if(ap) alumBin.push_back(ap);
    else if(pp) paperBin.push_back(pp);
    else if(gp) glassBin.push_back(gp);
    ++sorter;
  }
  sumValue(alumBin, cout);
  sumValue(paperBin, cout);
  sumValue(glassBin, cout);
  sumValue(bin, cout);
  purge(bin);
} ///:~

因為垃圾被不加分類地投入到一個容器,這樣一來,垃圾的所有信息就”丟失“了。但是,為了稍后適當?shù)貙U料進行分類,具體類型信息必須恢復,這將用到RTTI。

可以通過使用map來改進這種解決方案,該map將指向type_info對象的指針與一個包含Trash指針的vector關(guān)聯(lián)起來。因為映像需要一個能識別排序的判定函數(shù),這里提供了一個名為TInfoLess的結(jié)構(gòu),它調(diào)用type_info::before()。注意,這里必須對sumValue()進行不同的定義。

//{L} Trash
// Recyling with a map.
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <map>
#include <typeinfo>
#include <utility>
#include <vector>
#include "Trash.h"
#include "../purge.h"
using namespace std;
// Comparator for type_info pointers
struct TInfoLess {
  bool operator()(const type_info* t1, const type_info* t2)
  const { return t1->before(*t2); }
};
typedef map<const type_info*, vector<Trash*>, TInfoLess>
  TrashMap;
// Sums up the value of the Trash in a bin:
void sumValue(const TrashMap::value_type& p, ostream& os) {
  vector<Trash*>::const_iterator tally = p.second.begin();
  float val = 0;
  while(tally != p.second.end()) {
    val += (*tally)->weight() * (*tally)->value();
    os << "weight of "
       << p.first->name()  // type_info::name()
       << " = " << (*tally)->weight() << endl;
    ++tally;
  }
  os << "Total value = " << val << endl;
}
int main() {
  srand(time(0)); // Seed the random number generator
  TrashMap bin;
  // Fill up the Trash bin:
  for(int i = 0; i < 30; i++) {
    Trash* tp;
    switch(rand() % 3) {
      case 0 :
        tp = new Aluminum((rand() % 1000)/10.0);
        break;
      case 1 :
        tp = new Paper((rand() % 1000)/10.0);
        break;
      case 2 :
        tp = new Glass((rand() % 1000)/10.0);
        break;
    }
    bin[&typeid(*tp)].push_back(tp);
  }
  // Print sorted results
  for(TrashMap::iterator p = bin.begin();
      p != bin.end(); ++p) {
    sumValue(*p, cout);
    purge(p->second);
  }
} ///:~

為了直接調(diào)用type_info::name(),我們在這里修改了sunValue(),因為作為TrashMap::value_type對的第1個成員,type_info對象現(xiàn)在是可獲得的。這樣就避免了為了獲得正在處理的Trash的類型名而額外調(diào)用typeid,而這在該程序的以前版本中卻是必須做的。

5.RTTI的機制和開銷

實現(xiàn)RTTI典型的方法是,通過在類的虛函數(shù)表中放置一個附加的指針。這個指針指向那個特別類型的type_info結(jié)構(gòu)。typeid()表達式的結(jié)果非常簡單:虛函數(shù)表指針取得type_info指針,并且產(chǎn)生一個對type_info結(jié)構(gòu)的引用。因為這正好是一個雙指針的解析操作,這是一個代價為常量時間的操作。

6.小結(jié)

盡管通常情況下會為一個指向其基類的指針進行向上類型轉(zhuǎn)換,然后再使用那個基類的通用接口(通過虛函數(shù)),但是如果知道一個由基類指針指向的對象的動態(tài)類型,有時候根據(jù)獲得的這些信息進行相關(guān)處理可能會使事情變得更加有效,而這些正是RTTI所提供的。大部分通常的誤用來自一些程序員,這些誤用是由于他們不理解虛函數(shù)而實采用RTTI來做類型檢查的編碼所造成的。C++的基本原理似乎提供了對違反類型的定義規(guī)則和完整性的情況進行監(jiān)督和糾正的強有力的工具和保護,但是如果有誰想故意誤用或回避某一語言的特征,那么將沒有人可以組織他這樣做。

到此這篇關(guān)于C++運行時類型識別與轉(zhuǎn)換實現(xiàn)方法的文章就介紹到這了,更多相關(guān)C++類型識別內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • OPENCV批量讀取圖片實現(xiàn)方法

    OPENCV批量讀取圖片實現(xiàn)方法

    下面小編就為大家?guī)硪黄狾PENCV批量讀取圖片實現(xiàn)方法。小編覺得挺不錯的。現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • Qt實現(xiàn)解壓帶有密碼的加密文件

    Qt實現(xiàn)解壓帶有密碼的加密文件

    Quazip是Qt平臺下面的一個壓縮解壓縮庫。本文將利用Quazip實現(xiàn)解壓帶有密碼的加密文件,文中的示例代碼講解詳細,感興趣的小伙伴可以了解一下
    2022-02-02
  • 基于Qt制作一個定時關(guān)機的小程序

    基于Qt制作一個定時關(guān)機的小程序

    這篇文章主要為大家詳細介紹了如何基于Qt制作一個有趣的定時關(guān)機的小程序,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下
    2023-12-12
  • 詳解c++11新特性之模板的改進

    詳解c++11新特性之模板的改進

    這篇文章主要介紹了詳解c++11新特性之模板的改進,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-05-05
  • 詳解Matlab繪制3D玫瑰花的方法(內(nèi)附旋轉(zhuǎn)版本)

    詳解Matlab繪制3D玫瑰花的方法(內(nèi)附旋轉(zhuǎn)版本)

    這篇文章主要為大家介紹了如何利用Matlab繪制3D版的玫瑰花以及旋轉(zhuǎn)版的3D玫瑰花,文中的示例代碼講解詳細,感興趣的小伙伴可以動手試一試
    2022-03-03
  • C語言樸素模式匹配算法實例代碼

    C語言樸素模式匹配算法實例代碼

    樸素模式匹配算法也稱為布魯特-福斯算法,感覺很是高大上,但是實現(xiàn)起來很簡單。這篇文章主要給大家介紹了關(guān)于C語言樸素模式匹配算法的相關(guān)資料,需要的朋友可以參考下
    2021-06-06
  • 淺談C++空間配置器allocator

    淺談C++空間配置器allocator

    在STL中,Memory Allocator處于最底層的位置,為一切的Container提供存儲服務(wù),是一切其他組件的基石。對于一般使用 STL 的用戶而言,Allocator是不可見的。本文將主要介紹C++空間配置器allocator
    2021-06-06
  • C++深入講解初始化列表的用法

    C++深入講解初始化列表的用法

    這篇文章主要介紹了C++成員初始化列表,除了可以使用構(gòu)造函數(shù)對類成員進行初始化之外,C++還提供了另外一種初始化的方法,叫做成員初始化列表。下面來看看文章的詳細吧,需要的朋友可以參考一下
    2022-04-04
  • c++代碼實現(xiàn)tea加密算法的實例詳解

    c++代碼實現(xiàn)tea加密算法的實例詳解

    這篇文章主要介紹了c++代碼實現(xiàn)tea加密算法,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-04-04
  • C++撲克牌的洗牌發(fā)牌游戲設(shè)計

    C++撲克牌的洗牌發(fā)牌游戲設(shè)計

    這篇文章主要為大家詳細介紹了C++撲克牌的洗牌發(fā)牌游戲設(shè)計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-08-08

最新評論