STLC++string类不能使⽤memcpy,memset等⼀序列内存操作
前⾔memset,memcpy
在C语⾔中,经常需要对内存进⾏操作,⾥⾯涉及到很多函数,但是memset函数的使⽤需要注意。
函数原型: void *memset(voidd *s, int ch, size_t n);
函数功能是:将s所指向的某⼀块内存中的前n个字节的内容全部设置为ch指定的ASCII值, 第⼀个值为指定的内存地址,块的⼤⼩由第三个参数指定,这个函数通常为新申请的内存做初始化⼯作, 其返回值为指向s的指针,它是对较⼤的结构体或数组进⾏清零操作的⼀种最快⽅法。
头⽂件: <memory.h>或者<string.h>
memset函数通常⽤来对已经分配好地址的内存进⾏初始化,并且通常初始化为0或者’\0’,(实际上是⼀样的)。
注意:
memset中的第三个参数⼀定要使⽤sizeof
memset的第⼀个参数⼀定是已知的、已经被分配内存的地址
⼤家可能⽐较疑惑,memset的第⼀个参数已经有了被初始化空间的⾸地址,为什么还要返回⼀个void*的指针去指向这个地址呢?这种结构在很多函数库⾥⾯⽐较常见,⽐如字符串操作函数等,都有类似的现象,这⾥之所以还要返回这个指针是为了实现链式编程,所谓链式编程,举个例⼦⼤家就明⽩了。
链式编程:
memcpy(cBuf1, memset(cBuf, 'a', sizeof(char) * 10), sizeof(char) * 10);
缺点:链式编程让代码显得不直观
最后⼀点,也是最重要的⼀点:
memset是按照字节字节进⾏赋值的
如果对int进⾏赋值,初始化为1,则会被初始化为0000 0001 0000 0001 0000 0001 0000 0001
⼀、memcpy,memset等内存操作的使⽤
对于memcpy函数 ,因为memcpy 执⾏的是浅拷贝,⽽String类因为⽤指针⾃⾏管理内存,是不能进⾏浅拷贝的。⽐如如下代码会发⽣访问异常:
std::wstring* pstrGameName = new std::wstring(L”⼤天使之剑”);
std::wstring* pstrCopyGameName = new std::wstring();
memcpy(pstrCopyGameName, pstrGameName , sizeof(std::wstring));
delete pstrGameName;
pstrGameName = NULL;
delete pstrCopyGameName;
pstrCopyGameName = NULL;
因为这两个string都是在堆区分配的,第⼀个string(* pstrGameName)在new的时候,会申请⼀块WCHAR* 数组,第⼆string在进⾏memcpy拷贝的时候,由于是浅拷贝,只是把* pstrCopyGameName的WCHAR* 指针指向了第⼀个string的WCHAR*数组。即两个string内部的WCH
AR指针指向了同⼀块内存区域。但分别delete掉两个string的时候 ,会对同⼀块WCHAR数据区域释放两次,所以会造成访问冲突,发⽣崩溃。
所以从这⾥要养成⼀个好的编程习惯,就是在定义struct结构体的时候,成员最好不要使⽤string类,⽽是⽤WCHAR数组来代替。同时不要使⽤memcpy函数,⽽是⾃⼰重装结构体的复制操作符,通过赋值操作符来对struct进⾏复制。
以下⽅式不推荐使⽤:
struct ClockInfo
{
std::wstring strGameName;
};
以下⽅式推荐使⽤:
struct ClockInfo
{
WCHAR szGameName[1024];
ClockInfo()
{
memset(szGameName, 0 , sizeof(szGameName));
}
ClockInfo& operator=(const ClockInfo& cInfo)
{
if(&cInfo != this)
{
memcpy(this->szGameName, cInfo.szGameName, sizeof(this->szGameName));
}
return *this;
}
};
⼆、浅拷贝深拷贝
浅拷贝就是对象的数据成员之间的简单赋值,如你设计了⼀个类,没有提供复制构造函数,当⽤该类的⼀个对象去给另⼀个对象赋值时所执⾏的过程就是浅拷贝,如:
class A
{
public:
A(int _data):
data=_data;
A(){}
private:
int data;
};
int main()
{
A a(5);
A b=a; //仅仅是数据成员之间的赋值
//这⼀句b=a;就是浅拷贝,执⾏完这句后b.data=5;
}
- 如果对象中没有其他的资源(如:堆,⽂件,系统资源等),则 深拷贝和浅拷贝没有什么区别,但当对象中有这些资源时,例⼦:
class A
{
public:
A(int _size)
{
size = _size;
data = new int[size]; // 假如其中有⼀段动态分配的内存
}
A(){};
~A()
{
delete [] data;
} // 析构时释放资源
private:
int* data;
int size;
};
int main()
{
A a(5);
A b = a; // 注意这⼀句
}
这⾥的b = a会造成未定义⾏为,因为类A中的复制构造函数是编译器⽣成的,所以b = a执⾏的是⼀个浅拷贝过程。我说过浅拷贝是对象数据之间的简单赋值,⽐如:
b.size=a.size;
b.data=a.data;
/*这⾥b的指针data和a的指针指向了堆上的同⼀⽚内存,
a和b析构时,b先把其data指向的动态分配的内存释放了⼀次,
⽽后a析构时⼜将这块已经被释放的内存再释放⼀次。
对同⼀块动态内存执⾏2次以上释放的结果是未定义的,所以这将导致内存泄漏或程序崩溃。
*/
这种情况就要⽤深拷贝来解决这个问题,深拷贝指的是当拷贝对象中有对其他资源(如堆、⽂件、系统等)的引⽤时(引⽤可以是指针或引⽤)时,对象另开辟⼀块新的资源,⽽不再对拷贝对象中有对其他资源的引⽤的指针或引⽤进⾏单纯的赋值。
例⼦如下:
class A
{
public:
A(int _size)
{
size=_size;
data=new int[size];
//假如其中有⼀段动态分配的内存
}
A(){};
sizeof 指针
A(const A& _A) //深拷贝
{
size=_A.size;
data=new int[size];
}
~A(){delete [] data;} //析构时释放资源
};
int main()
{
A a(5);
int b=a; //这次就没有问题了
}
总结:
深拷贝和浅拷贝的区别是在对象状态中包含其它对象的引⽤的时候,当拷贝⼀个对象时,如果需要拷贝这个对象引⽤的对象,则是深拷贝,否则是浅拷贝。
三、待续
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论