//C++学习笔记_06 深拷贝和浅拷贝 #include<cstdio> #include<cstring> #include<string> #include<iostream> using namespace std; void TestString() { char szName[12]; char *pName; //szName 的值不能改变,它的值,是栈内存的地址 //szName = "Jack"; //不允许这样赋值 strcpy(szName,"Jack"); //遇到 '\0' 结束拷贝 //memcpy(szName, "Jack", 12); //准确的拷贝 12个字节,不管里面什么内容 pName = "Lucy"; //这个是允许的 pName++; //===> pName 指向 "Lucy" 中的 'u' //szName++; //szName 存储的是函数调用栈内, 那段字符串的首地址,不可以修改 char *p = szName; //这个就是简单的赋值,把szName 的值(地址) 赋值给 p string s1 = "Kary"; string s2 = s1; string sss(100, 'a'); cout << "sss:" << endl; cout << "size :" << sss.size() << endl; cout << "sizeof:" << sizeof(sss) << endl; } class AAA { private: char szName[12]; //szName 存储的是栈内存的地址,这段地址长度是 12个字节 char *pInfo; //pInfo 没有赋值,指向的地址未知 public: AAA(){ memset(szName, 0, 12); pInfo = NULL; } void SetName(char *pStr) {strcpy(szName, pStr);} void SetInfo(char *pStr) { pInfo = pStr;} //把私有成员的指针,指向了一个外部地址。 //这是一个错误的写法,非常危险,可能导致访问或者修改了别人的内存 //在外部 new 一片内存进行赋值 --》释放内存的时候,不能不释放,也不能多释放 void ToUpper(){ char *p=szName; while (*p) {//这个退出条件就是 *p==0 ('\0') if (*p>='a' && *p<='z')(*p)-=32; p++; } p=pInfo; while (*p) {//这个退出条件就是 *p==0 ('\0') if (*p>='a' && *p<='z')(*p)-=32; p++; } } void PrintName() { cout << "Name:" << szName << endl;} void PrintInfo() { cout << "Info:" << pInfo << endl;} }; void TestAAA() { AAA A1; AAA A2; AAA A3; char szName[] = "Lucy"; A1.SetName("Jack"); A1.SetInfo(szName); A2 = A1; //这个拷贝方式:直接把成员变量的内存值复制过来 //相当于 memcpy(&A2, &A1, sizeof(AAA)); szName[0] = 'Q'; cout << "A1:" << endl; A1.PrintName(); A1.PrintInfo(); cout << endl; cout << "A2:" << endl; A2.PrintName(); A2.PrintInfo(); cout << endl; A1.ToUpper(); //调用 A1 的成员函数,把 A2 的成员变量改了 cout << "A1:" << endl; A1.PrintName(); A1.PrintInfo(); cout << endl; cout << "A2:" << endl; A2.PrintName(); A2.PrintInfo(); cout << endl; cout << "外部szName:" << szName << endl; return; } void ModifyAAA(AAA &A) { char szName[] = "Jerry"; A.SetInfo(szName); return; } class BBB { private: char *pInfo; public: BBB(){ cout << "构造函数分配内存" << endl; pInfo = new char[1000]; //分配内存 memset(pInfo, 0, 1000); } BBB(char szInfo[]){ cout << "构造函数分配内存" << endl; pInfo = new char[1000]; memset(pInfo, 0, 1000); strcpy(pInfo,szInfo); } BBB(const BBB &B) {//拷贝构造函数, 入参是 BBB 的对象 cout << "拷贝构造函数分配内存" << endl; pInfo = new char[1000]; strcpy(pInfo,B.pInfo); //把 B 中的数据拷贝过来 } ~BBB() { //前面几节课写的类,析构函数都是空的 //析构函数的用处是释放资源,构造函数里申请了资源,在这里我们要释放掉 cout << "析构函数释放内存" << endl; delete[] pInfo; } void SetInfo(char szInfo[]){ strcpy(pInfo,szInfo); } void Print(){ cout << pInfo << endl; } }; void TestBBB() { BBB B1("C++面向对象"); BBB B2 = B1; //同样的,B1 的值修改了,会导致 B2修改 BBB B3(B1); //也是把 B1 赋值给 B3 //B2 = B1 和 B3(B1) 这两个方法在我们的 BBB 内没有定义 //系统会给 BBB 生成一个默认的 拷贝函数,操作就是把私有成员赋值过来 //这种拷贝方式,我们称为 浅拷贝 //对应的就有 深拷贝:涉及到申请内存的时候,我们自己定义拷贝构造函数 // 这个拷贝构造函数, 我们自己申请内存,然后把目标对象的内存地址内的数据拷贝过来 //string 的拷贝方式就是 深拷贝 cout << endl; cout << "B2:"; B2.Print(); cout << endl; B1.SetInfo("Hello world!"); cout << "B1:"; B1.Print(); cout << "B2:"; B2.Print(); cout << endl; //函数退出前,会调用 B1, B2 的析构函数 //B2 = B1 也就是说 B1,B2 的 pInfo指针,值是一样的,指向同一片内存 //内存只申请了一次,被两次释放 --》 出错 } void PrintString(const string &s){ cout << s << endl; } void TestCopyOnWrite(){ //事实上,string 这个类使用的拷贝技术更高效:写时拷贝 //使用 = 赋值的时候,内部指针指向同一片内存 //只有这片内存被修改的时候,才复制一份出来 string s1 = "Hello world!"; string s2 = s1; //s1 和 s2 内部内存都是指向一个 "Hello world 字符串" //s1.c_str(); //取出了内部字符串的存储地址 cout << "Addr S1:" << static_cast<const void*>(s1.c_str()) << endl; cout << "Addr S2:" << static_cast<const void*>(s2.c_str()) << endl; //这里编译器做了处理,s1 和 s2 内部指针指向了不同的地址 //写时拷贝:赋值的时候,不会申请新的内存,两个字符串,指向了同一片内存 //只有 字符串被修改的时候,才会申请新的内存,把数据拷贝完成后再修改 //s1[0] = 'K'; PrintString(s1); cout << "Addr S1:" << static_cast<const void*>(s1.c_str()) << endl; cout << "Addr S2:" << static_cast<const void*>(s2.c_str()) << endl; } int main() { //TestString(); //TestAAA(); //TestBBB(); TestCopyOnWrite(); system("pause"); return 0; }