CCString重点方法解析

| 分类 cocos2d-x  | 标签 cocos2d-x-2.2.3  C++ 
  1. CCString::operator= (const CCString& other)方法,关于=的重载是怎么实现的

    1. CCString& CCString::operator= (const CCString& other)
    2. {
    3. m_sString = other.m_sString;
    4. return *this;//返回CCString的引用
    5. }
  2. bool initWithFormatAndValist(const char* format, va_list ap)是一个private方法,说它主要是为了想学习下va_list

    1. bool CCString::initWithFormatAndValist(const char* format, va_list ap)
    2. {
    3. bool bRet = false;
    4. char* pBuf = (char*)malloc(kMaxStringLen);
    5. if (pBuf != NULL)
    6. {
    7. vsnprintf(pBuf, kMaxStringLen, format, ap);//将可变参数格式化输出到一个字符数组
    8. m_sString = pBuf;
    9. free(pBuf);
    10. bRet = true;
    11. }
    12. return bRet;
    13. }
    14. //备注:int_vsnprintf(char*str,size_tsize,constchar*format,va_listap);
    15. 参数说明:
    16. 1.char *str [out],把生成的格式化的字符串存放在这里.
    17. 2.size_t size [in], str可接受的最大字节数,防止产生数组越界.
    18. 3.const char *format [in], 指定输出格式的字符串,它决定了你需要提供的可变参数的类型、个数和顺序
    19. 4.va_list ap [in], va_list变量. va:variable-argument:可变参数
  3. bool CCString::initWithFormat(const char* format, …)这个方法应该很熟悉,将数据格式化成CCString类型,在这方法中回调了initWithFormatAndValist这个方法。

    1. bool CCString::initWithFormat(const char* format, ...)
    2. {
    3. bool bRet = false;
    4. m_sString.clear();//Erases the string, making it empty.
    5. va_list ap;//在调用参数表之前,定义一个 va_list 类型的变量
    6. va_start(ap, format);//对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数
    7. bRet = initWithFormatAndValist(format, ap);
    8. va_end(ap);// 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
    9. return bRet;
    10. }
    11. 备注:
    12. 1.C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
    13. void foo(...);
    14. void foo(parm_list,...);
    15. 这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。
    16. 2.函数参数的传递原理
    17. 函数参数是以数据结构:栈的形式存取,从右至左入栈。
    18. 首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址
    19. 举个例子如下:void func(int x, float y, char z);
    20. 那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
    21. 下面是 <stdarg.h> 里面重要的几个宏定义如下:
    22. 1. typedef char* va_list;
    23. 2. void va_start ( va_list ap, prev_param ); /* ANSI version */
    24. 3. type va_arg ( va_list ap, type );
    25. 4. void va_end ( va_list ap );
    26. va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
    27. <Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
    28. <Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
    29. <Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
    30. <Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_startva_end是成对出现。
    31. 1. _INTSIZEOF 宏,获取类型占用的空间长度,最小占用长度为int的整数倍:
    32. #define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
    33. 2. VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数)
    34. #define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
    35. 3. VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型)
    36. #define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
    37. 4. VA_END宏,清空va_list可变参数列表
    38. #define va_end(ap) ( ap = (va_list)0 )
    39. 例如 int max(int n, ...); 其函数内部应该如此实现:
    40. #include <iostream.h>
    41. void fun(int a, ...)
    42. {
    43. int *temp = &a;
    44. temp++;
    45. for (int i = 0; i < a; ++i)
    46. {
    47. cout << *temp << endl;
    48. temp++;
    49. }
    50. }
    51. int main()
    52. {
    53. int a = 1;
    54. int b = 2;
    55. int c = 3;
    56. int d = 4;
    57. fun(4, a, b, c, d);
    58. system("pause");
    59. return 0;
    60. }
    61. Output::
    62. 1
    63. 2
    64. 3
    65. 4
  4. int CCString::compare(const char * pStr)-pStr与CCString比较大小,真正比较的是字符串本身

    1. int CCString::compare(const char * pStr) const
    2. {
    3. return strcmp(getCString(), pStr);//通过strcmp来比较
    4. }
  5. CCObject* CCString::copyWithZone(CCZone* pZone)复制一份CCString

    1. CCObject* CCString::copyWithZone(CCZone* pZone)
    2. {
    3. CCAssert(pZone == NULL, "CCString should not be inherited.");
    4. CCString* pStr = new CCString(m_sString.c_str());//重新创建了一份CCString
    5. return pStr;
    6. }
    7. //备注:CCZone类基于Cocosd-x,在copyWithZone中传人,主要是CCZone拥有一个CCObject,下面是CCZone的主要代码:
    8. class CC_DLL CCZone
    9. {
    10. public:
    11. CCZone(CCObject *pObject = NULL);
    12. public:
    13. CCObject *m_pCopyObject;//当初始化CCZone的时候会将CCObject传入。
    14. };
    15. //备注:CCCopying实现该协议,就能复制对象
    16. class CC_DLL CCCopying
    17. {
    18. public:
    19. virtual CCObject* copyWithZone(CCZone* pZone);
    20. };
    21. //同时在CCObject中有这么个方法,用于复制对象
    22. CCObject* CCObject::copy()
    23. {
    24. return copyWithZone(0);//会回调各个实现了CCCopying的copyWithZone方法
    25. }
  6. bool CCString::isEqual(const CCObject* pObject)比较两个CCObject是否相等

    1. bool CCString::isEqual(const CCObject* pObject)
    2. {
    3. bool bRet = false;
    4. const CCString* pStr = dynamic_cast<const CCString*>(pObject);
    5. if (pStr != NULL)
    6. {
    7. if (0 == m_sString.compare(pStr->m_sString))
    8. {
    9. bRet = true;
    10. }
    11. }
    12. return bRet;
    13. }
    14. 备注:
    15. dynamic_cast < type-id > ( expression )
    16. 该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void*;
    17. 如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
    18. dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果downcast不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象)。
    19. dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
    20. 在类层次间进行上行转换时,dynamic_caststatic_cast的效果是一样的;
    21. 在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
    22. class B
    23. {
    24. public:
    25. int m_iNum;
    26. virtual void foo();
    27. };
    28. class D:public B
    29. {
    30. public:
    31. char* m_szName[100];
    32. };
    33. void func(B* pb)
    34. {
    35. D* pd1=static_cast<D*>(pb);
    36. D* pd2=dynamic_cast<D*>(pb);
    37. }
    38. 1.在上面的代码段中,如果pb指向一个D类型的对象,pd1pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;
    39. 2.但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),而pd2将是一个空指针.
    40. 3.另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。
    41. dynamic_cast还支持交叉转换(cross cast
    42. class A
    43. {
    44. public:
    45. int m_iNum;
    46. virtual void f(){}
    47. };
    48. class B:public A
    49. {
    50. };
    51. class D:public A
    52. {
    53. };
    54. void foo()
    55. {
    56. B* pb=new B;
    57. pb->m_iNum=100;
    58. D*pd1=static_cast<D*>(pb);//compile error
    59. D*pd2=dynamic_cast<D*>(pb);//pd2 is NULL
    60. delete pb;
    61. }
    62. 在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错,而使用 dynamic_cast的转换则是允许的,结果是空指针。
    63. static_cast < type-id > ( expression )
    64. 该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
    65. ①. 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
    66. ②. 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
    67. ③. 把空指针转换成目标类型的空指针。
    68. ④. 把任何类型的表达式转换成void类型。
    69. 注意:static_cast不能转换掉expressionconstvolatile、或者__unaligned属性。
    70. C++中的static_cast执行非多态的转换,用于代替C中通常的转换操作。因此,被做为显式类型转换使用。比如:
    71. int i;
    72. float f=166.71;
    73. i=static_cast<int>(f);
    74. 此时结果,i的值为166
    75. const_cast<type_id> (expression)
    76. 该运算符用来修改类型的constvolatile属性。除了const volatile修饰之外, type_idexpression的类型是一样的。
    77. 1. 常量指针被转化成非常量的指针,并且仍然指向原来的对象;
    78. 2. 常量引用被转换成非常量的引用,并且仍然指向原来的对象;
    79. volatileconst类似。举如下一例:
    80. class B
    81. {
    82. public:
    83. B() { }
    84. public:
    85. int m_iNum;
    86. };
    87. void foo()
    88. {
    89. const B b1;
    90. //b1.m_iNum = 100; //compile error
    91. // 可以做如下转换,体现出转换为指针类型
    92. B *b2 = const_cast<B*>(&b1);
    93. // 或者左侧也可以用引用类型,如果对b2或b3的数据成员做改变,就是对b1的值在做改变
    94. B &b3 = const_cast<B&>(b1);
    95. b2->m_iNum = 200; //fine
    96. b3.m_iNum = 300; //fine
    97. }
    98. int main( int argc, char * argv[] )
    99. {
    100. foo();
    101. return 0;
    102. }
    103. 使用const_cast可以返回一个指向非常量的指针(或引用)指向b1,就可以通过该指针(或引用)对它的数据成员任意改变。
  7. static CCString* createWithData(const unsigned char* pData, unsigned long nLen)将char*转换成CCString*类型

    1. CCString* CCString::createWithData(const unsigned char* pData, unsigned long nLen)
    2. {
    3. CCString* pRet = NULL;
    4. if (pData != NULL)
    5. {
    6. char* pStr = (char*)malloc(nLen+1);
    7. if (pStr != NULL)
    8. {
    9. pStr[nLen] = '\0';
    10. if (nLen > 0)
    11. {
    12. memcpy(pStr, pData, nLen);//c和c++使用的内存拷贝函数,memcpy函数的功能是从源pData所指的内存地址的起始位置开始拷贝nLen个字节到目标pStr所指的内存地址的起始位置中
    13. }
    14. pRet = CCString::create(pStr);
    15. free(pStr);//释放ptr指向的存储空间
    16. }
    17. }
    18. return pRet;
    19. }
    20. 备注:
    21. strcpymemcpy主要有以下3方面的区别。
    22. 1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
    23. 2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
    24. 3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
    25. 关于extern char *strcpy(char* dest, const char *src)方法,其功能是把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间,srcdest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串,返回指向dest的指针
  8. CCString* CCString::createWithContentsOfFile(const char* pszFileName)该方法主要是传入文件的名字,将该文件转成CCString*

    1. CCString* CCString::createWithContentsOfFile(const char* pszFileName)
    2. {
    3. unsigned long size = 0;
    4. unsigned char* pData = 0;
    5. CCString* pRet = NULL;
    6. pData = CCFileUtils::sharedFileUtils()->getFileData(pszFileName, "rb", &size);//通过该方法将pszFileName文件转换成char*
    7. pRet = CCString::createWithData(pData, size);//通过上文讲到的createWithData方法将pData转换成CCString*
    8. CC_SAFE_DELETE_ARRAY(pData);//#define CC_SAFE_DELETE_ARRAY(p) do { if(p) { delete[] (p); (p) = 0; } } while(0) 使用delete[]操作符删除一个C++数组p,如果p为NULL,则不进行操作
    9. return pRet;
    10. }
  9. unsigned char* CCFileUtils::getFileData(const char* pszFileName, const char* pszMode, unsigned long* pSize)该方法传入文件名,以及读写模式,读出该文件的大小,是long*类型

    1. unsigned char* CCFileUtils::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize)
    2. {
    3. unsigned char * pBuffer = NULL;
    4. CCAssert(pszFileName != NULL && pSize != NULL && pszMode != NULL, "Invalid parameters.");
    5. *pSize = 0;
    6. do
    7. {
    8. // read the file from hardware
    9. std::string fullPath = fullPathForFilename(pszFileName);//获取该文件的完成的路径
    10. FILE *fp = fopen(fullPath.c_str(), pszMode);//pszMode是指示文件读取方式的字符串:"r" = read "w" = write "rw" = read&write
    11. CC_BREAK_IF(!fp);
    12. fseek(fp,0,SEEK_END);//设置文件指针fp的位置,如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置,函数返回一个非0值。
    13. *pSize = ftell(fp);//使用fseek函数后再调用函数ftell()就能非常容易地确定文件的当前位置,当然将文件的当前位置移到文件的末尾,然后调用函数ftell()获得当前位置相对于文件首的位移,该位移值等于文件所含字节数
    14. fseek(fp,0,SEEK_SET);
    15. pBuffer = new unsigned char[*pSize];
    16. *pSize = fread(pBuffer,sizeof(unsigned char), *pSize,fp);//fread是一个函数。从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读取到的元素个数,如果不成功返回 0
    17. fclose(fp);//关闭一个流。注意:使用fclose()函数就可以把缓冲区内最后剩余的数据输出到磁盘文件中,并释放文件指针和有关的缓冲区。
    18. } while (0);
    19. if (! pBuffer)
    20. {
    21. std::string msg = "Get data from file(";
    22. msg.append(pszFileName).append(") failed!");
    23. CCLOG("%s", msg.c_str());
    24. }
    25. return pBuffer;
    26. }
    27. 备注:
    28. 1. FILE * fopen(const char * path,const char * mode);
    29. 文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL
    30. 2. int fseek( FILE *stream, long offset, int origin );
    31. 第一个参数stream为文件指针
    32. 第二个参数offset为偏移量,正数表示正向偏移,负数表示负向偏移
    33. 第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR SEEK_END SEEK_SET
    34. SEEK_SET 文件开头
    35. SEEK_CUR 当前位置
    36. SEEK_END 文件结尾
    37. 其中SEEK_SET,SEEK_CURSEEK_END依次为012.
    38. 简言之:
    39. fseek(fp,100L,0);把文件内部指针移动到离文件开头100字节处;
    40. fseek(fp,100L,1);把文件内部指针移动到离文件当前位置100字节处;
    41. fseek(fp,-100L,2);把文件内部指针退回到离文件结尾100字节处。
    42. 3. long ftell(FILE *stream);
    43. 使用fseek函数后再调用函数ftell()就能非常容易地确定文件的当前位置。
    44. 4. size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
    45. buffer 用于接收数据的内存地址
    46. size 要读写的字节数,单位是字节
    47. count 要进行读写多少个size字节的数据项,每个元素是size字节.
    48. stream 输入流
    49. 返回值:实际读取的元素个数.如果返回值与count不相同,则可能文件结尾或发生错误.从ferrorfeof获取错误信息或检测是否到达文件结尾.

关于CCString相关的方法,就讲到这。。。

青春是一场大雨,即使感冒了,还盼回头再淋一次...image...微笑永远是一个人身上最好看的东西...


PREVIOUS     NEXT