-
CCString::operator= (const CCString& other)方法,关于=的重载是怎么实现的
CCString& CCString::operator= (const CCString& other)
{
m_sString = other.m_sString;
return *this;//返回CCString的引用
}
-
bool initWithFormatAndValist(const char* format, va_list ap)是一个private方法,说它主要是为了想学习下va_list
bool CCString::initWithFormatAndValist(const char* format, va_list ap)
{
bool bRet = false;
char* pBuf = (char*)malloc(kMaxStringLen);
if (pBuf != NULL)
{
vsnprintf(pBuf, kMaxStringLen, format, ap);//将可变参数格式化输出到一个字符数组
m_sString = pBuf;
free(pBuf);
bRet = true;
}
return bRet;
}
//备注:int_vsnprintf(char*str,size_tsize,constchar*format,va_listap);
参数说明:
1.char *str [out],把生成的格式化的字符串存放在这里.
2.size_t size [in], str可接受的最大字节数,防止产生数组越界.
3.const char *format [in], 指定输出格式的字符串,它决定了你需要提供的可变参数的类型、个数和顺序
4.va_list ap [in], va_list变量. va:variable-argument:可变参数
-
bool CCString::initWithFormat(const char* format, …)这个方法应该很熟悉,将数据格式化成CCString类型,在这方法中回调了initWithFormatAndValist这个方法。
bool CCString::initWithFormat(const char* format, ...)
{
bool bRet = false;
m_sString.clear();//Erases the string, making it empty.
va_list ap;//在调用参数表之前,定义一个 va_list 类型的变量
va_start(ap, format);//对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数
bRet = initWithFormatAndValist(format, ap);
va_end(ap);// 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
return bRet;
}
备注:
1.在C中,当我们无法列出传递函数的所有实参的类型和数目时,可以用省略号指定参数表
void foo(...);
void foo(parm_list,...);
这种方式和我们以前认识的不大一样,但我们要记住这是C中一种传参的形式,在后面我们就会用到它。
2.函数参数的传递原理
函数参数是以数据结构:栈的形式存取,从右至左入栈。
首先是参数的内存存放格式:参数存放在内存的堆栈段中,在执行函数的时候,从最后一个开始入栈。因此栈底高地址,栈顶低地址
举个例子如下:void func(int x, float y, char z);
那么,调用函数的时候,实参 char z 先进栈,然后是 float y,最后是 int x,因此在内存中变量的存放次序是 x->y->z,因此,从理论上说,我们只要探测到任意一个变量的地址,并且知道其他变量的类型,通过指针移位运算,则总可以顺藤摸瓜找到其他的输入变量。
下面是 <stdarg.h> 里面重要的几个宏定义如下:
1. typedef char* va_list;
2. void va_start ( va_list ap, prev_param ); /* ANSI version */
3. type va_arg ( va_list ap, type );
4. void va_end ( va_list ap );
va_list 是一个字符指针,可以理解为指向当前参数的一个指针,取参必须通过这个指针进行。
<Step 1> 在调用参数表之前,定义一个 va_list 类型的变量,(假设va_list 类型变量被定义为ap);
<Step 2> 然后应该对ap 进行初始化,让它指向可变参数表里面的第一个参数,这是通过 va_start 来实现的,第一个参数是 ap 本身,第二个参数是在变参表前面紧挨着的一个变量,即“...”之前的那个参数;
<Step 3> 然后是获取参数,调用va_arg,它的第一个参数是ap,第二个参数是要获取的参数的指定类型,然后返回这个指定类型的值,并且把 ap 的位置指向变参表的下一个变量位置;
<Step 4> 获取所有的参数之后,我们有必要将这个 ap 指针关掉,以免发生危险,方法是调用 va_end,他是输入的参数 ap 置为 NULL,应该养成获取完参数表之后关闭指针的习惯。说白了,就是让我们的程序具有健壮性。通常va_start和va_end是成对出现。
1. _INTSIZEOF 宏,获取类型占用的空间长度,最小占用长度为int的整数倍:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
2. VA_START宏,获取可变参数列表的第一个参数的地址(ap是类型为va_list的指针,v是可变参数最左边的参数)
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
3. VA_ARG宏,获取可变参数的当前参数,返回指定类型并将指针指向下一参数(t参数描述了当前参数的类型)
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
4. VA_END宏,清空va_list可变参数列表
#define va_end(ap) ( ap = (va_list)0 )
例如 int max(int n, ...); 其函数内部应该如此实现:
#include <iostream.h>
void fun(int a, ...)
{
int *temp = &a;
temp++;
for (int i = 0; i < a; ++i)
{
cout << *temp << endl;
temp++;
}
}
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
fun(4, a, b, c, d);
system("pause");
return 0;
}
Output::
1
2
3
4
-
int CCString::compare(const char * pStr)-pStr与CCString比较大小,真正比较的是字符串本身
int CCString::compare(const char * pStr) const
{
return strcmp(getCString(), pStr);//通过strcmp来比较
}
-
CCObject* CCString::copyWithZone(CCZone* pZone)复制一份CCString
CCObject* CCString::copyWithZone(CCZone* pZone)
{
CCAssert(pZone == NULL, "CCString should not be inherited.");
CCString* pStr = new CCString(m_sString.c_str());//重新创建了一份CCString
return pStr;
}
//备注:CCZone类基于Cocosd-x,在copyWithZone中传人,主要是CCZone拥有一个CCObject,下面是CCZone的主要代码:
class CC_DLL CCZone
{
public:
CCZone(CCObject *pObject = NULL);
public:
CCObject *m_pCopyObject;//当初始化CCZone的时候会将CCObject传入。
};
//备注:CCCopying实现该协议,就能复制对象
class CC_DLL CCCopying
{
public:
virtual CCObject* copyWithZone(CCZone* pZone);
};
//同时在CCObject中有这么个方法,用于复制对象
CCObject* CCObject::copy()
{
return copyWithZone(0);//会回调各个实现了CCCopying的copyWithZone方法
}
-
bool CCString::isEqual(const CCObject* pObject)比较两个CCObject是否相等
bool CCString::isEqual(const CCObject* pObject)
{
bool bRet = false;
const CCString* pStr = dynamic_cast<const CCString*>(pObject);
if (pStr != NULL)
{
if (0 == m_sString.compare(pStr->m_sString))
{
bRet = true;
}
}
return bRet;
}
备注:
dynamic_cast < type-id > ( expression )
该运算符把expression转换成type-id类型的对象。Type-id必须是类的指针、类的引用或者void*;
如果type-id是类指针类型,那么expression也必须是一个指针,如果type-id是一个引用,那么expression也必须是一个引用。
dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的(也就说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果downcast不安全,这个运算符会传回空指针(也就是说,基类指针或者引用没有指向一个派生类对象)。
dynamic_cast主要用于类层次间的上行转换和下行转换,还可以用于类之间的交叉转换。
在类层次间进行上行转换时,dynamic_cast和static_cast的效果是一样的;
在进行下行转换时,dynamic_cast具有类型检查的功能,比static_cast更安全。
class B
{
public:
int m_iNum;
virtual void foo();
};
class D:public B
{
public:
char* m_szName[100];
};
void func(B* pb)
{
D* pd1=static_cast<D*>(pb);
D* pd2=dynamic_cast<D*>(pb);
}
1.在上面的代码段中,如果pb指向一个D类型的对象,pd1和pd2是一样的,并且对这两个指针执行D类型的任何操作都是安全的;
2.但是,如果pb指向的是一个B类型的对象,那么pd1将是一个指向该对象的指针,对它进行D类型的操作将是不安全的(如访问m_szName),而pd2将是一个空指针.
3.另外要注意:B要有虚函数,否则会编译出错;static_cast则没有这个限制。
dynamic_cast还支持交叉转换(cross cast)
class A
{
public:
int m_iNum;
virtual void f(){}
};
class B:public A
{
};
class D:public A
{
};
void foo()
{
B* pb=new B;
pb->m_iNum=100;
D*pd1=static_cast<D*>(pb);//compile error
D*pd2=dynamic_cast<D*>(pb);//pd2 is NULL
delete pb;
}
在函数foo中,使用static_cast进行转换是不被允许的,将在编译时出错,而使用 dynamic_cast的转换则是允许的,结果是空指针。
static_cast < type-id > ( expression )
该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性。它主要有如下几种用法:
①. 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换。进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。
②. 用于基本数据类型之间的转换,如把int转换成char,把int转换成enum。这种转换的安全性也要开发人员来保证。
③. 把空指针转换成目标类型的空指针。
④. 把任何类型的表达式转换成void类型。
注意:static_cast不能转换掉expression的const、volatile、或者__unaligned属性。
C++中的static_cast执行非多态的转换,用于代替C中通常的转换操作。因此,被做为显式类型转换使用。比如:
int i;
float f=166.71;
i=static_cast<int>(f);
此时结果,i的值为166。
const_cast<type_id> (expression)
该运算符用来修改类型的const或volatile属性。除了const 或volatile修饰之外, type_id和expression的类型是一样的。
1. 常量指针被转化成非常量的指针,并且仍然指向原来的对象;
2. 常量引用被转换成非常量的引用,并且仍然指向原来的对象;
volatile和const类似。举如下一例:
class B
{
public:
B() { }
public:
int m_iNum;
};
void foo()
{
const B b1;
//b1.m_iNum = 100; //compile error
// 可以做如下转换,体现出转换为指针类型
B *b2 = const_cast<B*>(&b1);
// 或者左侧也可以用引用类型,如果对b2或b3的数据成员做改变,就是对b1的值在做改变
B &b3 = const_cast<B&>(b1);
b2->m_iNum = 200; //fine
b3.m_iNum = 300; //fine
}
int main( int argc, char * argv[] )
{
foo();
return 0;
}
使用const_cast可以返回一个指向非常量的指针(或引用)指向b1,就可以通过该指针(或引用)对它的数据成员任意改变。
-
static CCString* createWithData(const unsigned char* pData, unsigned long nLen)将char*转换成CCString*类型
CCString* CCString::createWithData(const unsigned char* pData, unsigned long nLen)
{
CCString* pRet = NULL;
if (pData != NULL)
{
char* pStr = (char*)malloc(nLen+1);
if (pStr != NULL)
{
pStr[nLen] = '\0';
if (nLen > 0)
{
memcpy(pStr, pData, nLen);//c和c++使用的内存拷贝函数,memcpy函数的功能是从源pData所指的内存地址的起始位置开始拷贝nLen个字节到目标pStr所指的内存地址的起始位置中
}
pRet = CCString::create(pStr);
free(pStr);//释放ptr指向的存储空间
}
}
return pRet;
}
备注:
strcpy和memcpy主要有以下3方面的区别。
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
关于extern char *strcpy(char* dest, const char *src)方法,其功能是把从src地址开始且含有NULL结束符的字符串复制到以dest开始的地址空间,src和dest所指内存区域不可以重叠且dest必须有足够的空间来容纳src的字符串,返回指向dest的指针
-
CCString* CCString::createWithContentsOfFile(const char* pszFileName)该方法主要是传入文件的名字,将该文件转成CCString*
CCString* CCString::createWithContentsOfFile(const char* pszFileName)
{
unsigned long size = 0;
unsigned char* pData = 0;
CCString* pRet = NULL;
pData = CCFileUtils::sharedFileUtils()->getFileData(pszFileName, "rb", &size);//通过该方法将pszFileName文件转换成char*
pRet = CCString::createWithData(pData, size);//通过上文讲到的createWithData方法将pData转换成CCString*
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,则不进行操作
return pRet;
}
-
unsigned char* CCFileUtils::getFileData(const char* pszFileName, const char* pszMode, unsigned long* pSize)该方法传入文件名,以及读写模式,读出该文件的大小,是long*类型
unsigned char* CCFileUtils::getFileData(const char* pszFileName, const char* pszMode, unsigned long * pSize)
{
unsigned char * pBuffer = NULL;
CCAssert(pszFileName != NULL && pSize != NULL && pszMode != NULL, "Invalid parameters.");
*pSize = 0;
do
{
// read the file from hardware
std::string fullPath = fullPathForFilename(pszFileName);//获取该文件的完成的路径
FILE *fp = fopen(fullPath.c_str(), pszMode);//pszMode是指示文件读取方式的字符串:"r" = read "w" = write "rw" = read&write
CC_BREAK_IF(!fp);
fseek(fp,0,SEEK_END);//设置文件指针fp的位置,如果执行成功,stream将指向以fromwhere为基准,偏移offset(指针偏移量)个字节的位置,函数返回0。如果执行失败(比如offset超过文件自身大小),则不改变stream指向的位置,函数返回一个非0值。
*pSize = ftell(fp);//使用fseek函数后再调用函数ftell()就能非常容易地确定文件的当前位置,当然将文件的当前位置移到文件的末尾,然后调用函数ftell()获得当前位置相对于文件首的位移,该位移值等于文件所含字节数
fseek(fp,0,SEEK_SET);
pBuffer = new unsigned char[*pSize];
*pSize = fread(pBuffer,sizeof(unsigned char), *pSize,fp);//fread是一个函数。从一个文件流中读数据,最多读取count个元素,每个元素size字节,如果调用成功返回实际读取到的元素个数,如果不成功返回 0
fclose(fp);//关闭一个流。注意:使用fclose()函数就可以把缓冲区内最后剩余的数据输出到磁盘文件中,并释放文件指针和有关的缓冲区。
} while (0);
if (! pBuffer)
{
std::string msg = "Get data from file(";
msg.append(pszFileName).append(") failed!");
CCLOG("%s", msg.c_str());
}
return pBuffer;
}
备注:
1. FILE * fopen(const char * path,const char * mode);
文件顺利打开后,指向该流的文件指针就会被返回。如果文件打开失败则返回NULL
2. int fseek( FILE *stream, long offset, int origin );
第一个参数stream为文件指针
第二个参数offset为偏移量,正数表示正向偏移,负数表示负向偏移
第三个参数origin设定从文件的哪里开始偏移,可能取值为:SEEK_CUR、 SEEK_END 或 SEEK_SET
SEEK_SET: 文件开头
SEEK_CUR: 当前位置
SEEK_END: 文件结尾
其中SEEK_SET,SEEK_CUR和SEEK_END依次为0,1和2.
简言之:
fseek(fp,100L,0);把文件内部指针移动到离文件开头100字节处;
fseek(fp,100L,1);把文件内部指针移动到离文件当前位置100字节处;
fseek(fp,-100L,2);把文件内部指针退回到离文件结尾100字节处。
3. long ftell(FILE *stream);
使用fseek函数后再调用函数ftell()就能非常容易地确定文件的当前位置。
4. size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;
buffer 用于接收数据的内存地址
size 要读写的字节数,单位是字节
count 要进行读写多少个size字节的数据项,每个元素是size字节.
stream 输入流
返回值:实际读取的元素个数.如果返回值与count不相同,则可能文件结尾或发生错误.从ferror和feof获取错误信息或检测是否到达文件结尾.
关于CCString相关的方法,就讲到这。。。
青春是一场大雨,即使感冒了,还盼回头再淋一次......微笑永远是一个人身上最好看的东西...