让C++的对象支持多类型
借助 C/C++ 的union,可以设计出近似弱类型的变量,同一类型的变量可以承载不同类型的数据。比如说一个对象A,可以按如下不同的类型使用: 
A a = 1; 
A b = 1.1; 
A c = "abc"; 
A d = true; 
使用的时候可以按照其真实的类型来使用。比如字符串c以调用c.size()获得其长度。 
这个想法来源于两个开源库的基础数据类型设计,一个是xmlrpclib库中XmlRpcValue设计,一个是xpdf中Object设计。非常 
的巧妙。核心在于定义一个union承载实际的数据,定义一个enum来标识数据实际的类型。 
XmlRpcValue的数据承载部分设计为: 
union { 
bool asBool; 
int asInt; 
double asDouble; 
struct tm* asTime; 
std::string* asString; 
BinaryData* asBinary; 
ValueArray* asArray; 
ValueStruct* asStruct; 
} _value; 
支持的类型如下定义: 
enum Type { 
TypeInvalid, 
TypeBoolean, 
TypeInt, 
TypeDouble, 
TypeString, 
TypeDateTime, 
TypeBase64, 
TypeArray, 
TypeStruct 
}; 
在使用此类对象的时候,先设置该对象的类型,然后再根据实际的类型进行运算。其实质仍然是严格类型的。但是从使用者 
的角度看来,却是弱类型的。 
此类对象的使用会对效率和空间有一定的影响。但影响都不大。 
时间方面的影响主要在于很多时候需要进行类型判定,若类型不匹配,则无法完成运算。值得注意的是,很多类型匹配可以 
在编译期间完成。比如,XmpRpcValue a = 1; XmlRpcValue b = true; 
空间方面主要是union分配空间是以最大的成员进行分配,但是如果大量使用指针,空间的多余耗费则不会很大。 
xmlrpclib库中XmlRpcValue核心代码如下(已删除部分不相关代码) 
class XmlRpcValue { 
public: 
enum Type { 
TypeInvalid, 
TypeBoolean, 
TypeInt, 
TypeDouble, 
TypeString, 
TypeDateTime, 
TypeBase64, 
TypeArray, 
TypeStruct 
}; 
// Non-primitive types 
typedef std::vector<char> BinaryData; 
typedef std::vector<XmlRpcValue> ValueArray; 
typedef std::map<std::string, XmlRpcValue> ValueStruct; 
//! Constructors 
XmlRpcValue() : _type(TypeInvalid) { _value.asBinary = 0; } 
XmlRpcValue(bool value) : _type(TypeBoolean) { _value.asBool = value; } 
XmlRpcValue(int value) : _type(TypeInt) { _value.asInt = value; } 
XmlRpcValue(double value) : _type(TypeDouble) { _value.asDouble = value; } 
XmlRpcValue(std::string const& value) : _type(TypeString) 
{ _value.asString = new std::string(value); } 
XmlRpcValue(const char* value) : _type(TypeString) 
{ _value.asString = new std::string(value); } 
XmlRpcValue(struct tm* value) : _type(TypeDateTime) 
{ _value.asTime = new struct tm(*value); } 
XmlRpcValue(void* value, int nBytes) : _type(TypeBase64) 
{ 
_value.asBinary = new BinaryData((char*)value, ((char*)value)+nBytes); 
} 
//! Construct from xml, beginning at *offset chars into the string, updates offset 
XmlRpcValue(std::string const& xml, int* offset) : _type(TypeInvalid) 
{ if ( ! fromXml(xml,offset)) _type = TypeInvalid; } 
//! Copy 
XmlRpcValue(XmlRpcValue const& rhs) : _type(TypeInvalid) { *this = rhs; } 
//! Destructor (make virtual if you want to subclass) 
/*virtual*/ ~XmlRpcValue() { invalidate(); } 
//! Erase the current value 
void clear() { invalidate(); } 
// Operators 
XmlRpcValue& operator=(XmlRpcValue const& rhs); 
XmlRpcValue& operator=(int const& rhs) { return operator=(XmlRpcValue(rhs)); } 
XmlRpcValue& operator=(double const& rhs) { return operator=(XmlRpcValue(rhs)); } 
XmlRpcValue& operator=(const char* rhs) { return operator=(XmlRpcValue(std::string(rhs))); } 
bool operator==(XmlRpcValue const& other) const; 
bool operator!=(XmlRpcValue const& other) const; 
operator bool&() { assertTypeOrInvalid(TypeBoolean); return _value.asBool; } 
operator int&() { assertTypeOrInvalid(TypeInt); return _value.asInt; } 
operator double&() { assertTypeOrInvalid(TypeDouble); return _value.asDouble; } 
operator std::string&() { assertTypeOrInvalid(TypeString); return *_value.asString; } 
operator BinaryData&() { assertTypeOrInvalid(TypeBase64); return *_value.asBinary; } 
operator struct tm&() { assertTypeOrInvalid(TypeDateTime); return *_value.asTime; } 
XmlRpcValue const& operator[](int i) const { assertArray(i+1); return _value.asArray->at(i); } 
XmlRpcValue& operator[](int i) { assertArray(i+1); return _value.asArray->at(i); } 
XmlRpcValue& operator[](std::string const& k) { assertStruct(); return (*_value.asStruct)[k]; } 
XmlRpcValue& operator[](const char* k) { assertStruct(); std::string s(k); return (*_value.asStruct)[s]; 
} 
// Accessors 
//! Return true if the value has been set to something. 
bool valid() const { return _type != TypeInvalid; }  //! Return the type of the value stored. \see Type. 
Type const &getType() const { return _type; } 
//! Return the size for string, base64, array, and struct values. 
int size() const; 
//! Specify the size for array values. Array values will grow beyond this size if needed. 
void setSize(int size) { assertArray(size); } 
//! Check for the existence of a struct member by name. 
bool hasMember(const std::string& name) const; 
// Type tag and values 
Type _type; 
// At some point I will split off Arrays and Structs into 
// separate ref-counted objects for more efficient copying. 
union { 
bool asBool; 
int asInt; 
double asDouble; 
struct tm* asTime; 
std::string* asString; 
BinaryData* asBinary; 
ValueArray* asArray; 
ValueStruct* asStruct; 
} _value; 
}; 
xpdf中Object的核心代码如下 
enum ObjType { 
// simple objects 
objBool, // boolean 
objInt, // integer 
objReal, // real 
objString, // string 
objName, // name 
objNull, // null 
// complex objects 
objArray, // array 
objDict, // dictionary 
objStream, // stream 
objRef, // indirect reference 
// special objects 
objCmd, // command name 
objError, // error return from Lexer 
objEOF, // end of file return from Lexer 
objNone // uninitialized object 
}; 
class Object { 
public: 
// Default constructor. 
Object(): 
type(objNone) {} 
// Initialize an object. 
Object *initBool(GBool boolnA) 
{ initObj(objBool); booln = boolnA; return this; } 
Object *initInt(int intgA) 
{ initObj(objInt); intg = intgA; return this; } 
Object *initReal(double realA) 
{ initObj(objReal); real = realA; return this; } 
Object *initString(GString *stringA) 
{ initObj(objString); string = stringA; return this; } 
Object *initName(char *nameA) 
{ initObj(objName); name = copyString(nameA); return this; } 
Object *initNull() 
{ initObj(objNull); return this; } 
Object *initArray(XRef *xref); 
Object *initDict(XRef *xref); 
Object *initDict(Dict *dictA); 
Object *initStream(Stream *streamA); 
Object *initRef(int numA, int genA) 
{ initObj(objRef); ref.num = numA; ref.gen = genA; return this; } 
Object *initCmd(char *cmdA) 
{ initObj(objCmd); cmd = copyString(cmdA); return this; } 
Object *initError() 
{ initObj(objError); return this; } 
Object *initEOF() 
{ initObj(objEOF); return this; } 
// Copy an object. 
Object *copy(Object *obj); 
// If object is a Ref, fetch and return the referenced object. 
// Otherwise, return a copy of the object. 
Object *fetch(XRef *xref, Object *obj); 
// Free object contents. 
void free(); 
// Type checking. 
ObjType getType() { return type; } 
GBool isBool() { return type == objBool; } 
GBool isInt() { return type == objInt; } 
GBool isReal() { return type == objReal; } 
GBool isNum() { return type == objInt || type == objReal; } 
GBool isString() { return type == objString; } 
GBool isName() { return type == objName; } 
GBool isNull() { return type == objNull; } 
GBool isArray() { return type == objArray; } 
GBool isDict() { return type == objDict; } 
GBool isStream() { return type == objStream; } 
GBool isRef() { return type == objRef; } 
GBool isCmd() { return type == objCmd; } 
GBool isError() { return type == objError; } 
GBool isEOF() { return type == objEOF; } 
GBool isNone() { return type == objNone; } 
// Special type checking. 
GBool isName(char *nameA) 
{ return type == objName && !strcmp(name, nameA); } 
GBool isDict(char *dictType); 
GBool isStream(char *dictType); 
GBool isCmd(char *cmdA) 
{ return type == objCmd && !strcmp(cmd, cmdA); } 
// Accessors. NB: these assume object is of correct type. 
GBool getBool() { return booln; } 
int getInt() { return intg; } 
double getReal() { return real; } 
double getNum() { return type == objInt ? (double)intg : real; } 
GString *getString() { return string; } 
char *getName() { return name; } 
Array *getArray() { return array; } 
Dict *getDict() { return dict; } 
Stream *getStream() { return stream; } 
Ref getRef() { return ref; } 
int getRefNum() { return ref.num; } 
int getRefGen() { return ref.gen; } 
char *getCmd() { return cmd; } 
// Array accessors. 
int arrayGetLength(); 
void arrayAdd(Object *elem); 
Object *arrayGet(int i, Object *obj); 
Object *arrayGetNF(int i, Object *obj); 
// Dict accessors. 
int dictGetLength(); 
void dictAdd(char *key, Object *val); 
GBool dictIs(char *dictType); 
Object *dictLookup(char *key, Object *obj);
char *dictGetKey(int i);
Object *dictGetVal(int i, Object *obj);
Object *dictGetValNF(int i, Object *obj);
// Stream accessors.
GBool streamIs(char *dictType);
void streamReset();
void streamClose();
int streamGetChar();
int streamLookChar();
char *streamGetLine(char *buf, int size);
Guint streamGetPos();
void streamSetPos(Guint pos, int dir = 0);
Dict *streamGetDict();
private:
ObjType type; // object type
union { // value for each type:
GBool booln; // boolean
int intg; // integer
double real; // real
GString *string; // string
char *name; // name
Array *array; // array
Dict *dict; // dictionary
Stream *stream; // stream
Ref ref; // indirect reference
char *cmd; // command
};
};
 
 
 减小字体
减小字体 增大字体
增大字体



 中查找“让C++的对象支持多类型”更多相关内容
中查找“让C++的对象支持多类型”更多相关内容 中查找“让C++的对象支持多类型”更多相关内容
中查找“让C++的对象支持多类型”更多相关内容