//拷贝有两种:深拷贝和浅拷贝
//1.结构体中的深拷贝和浅拷贝
/*
浅拷贝:编译器仅仅拷贝了结构体的值,而没有创建新的内存空间,而是共享同一块内存空间
。当结构体成员中含有buf的时候,拷贝之后释放内存就不会出现问题,但是如果结构体中含有指针变量时,
编译器就只会拷贝指针变量,而对应
的,内存空间却不会缺,不再多分配。
深拷贝:
编译器会为拷贝的对象分配一定的内存空间
-
#include <iostream>
-
using namespace std;
-
/*
-
struct Teacher
-
{
-
string name;
-
int age;
-
}Teacher;
-
int main()
-
{
-
struct Teacher t1={"cang",18};
-
struct Teacher t2={"li",20};
-
t2=t1;//浅拷贝,没有为t2创建内存空间
-
cout<<"age is:"<<t2.age<<"name is:"<<t2.name<<endl;
-
return 0;
-
}
-
*/
-
//上面的结构体可以直接使用等于号进行赋值操作;
-
/*
-
#include <iostream>
-
#include <stdio.h>
-
using namespace std;
-
struct Teacher
-
{
-
int id;
-
char sex;
-
const char *p;
-
}Teacher;
-
int main()
-
{
-
struct Teacher t1, t2;
-
//const 修饰加上,因为const 存储在常量区,不能被修改,通过指针修改比较危险
-
//所以通过前面加上const之后,警告消失
-
const char *str ="i am string";
-
t1.id = 345;
-
t1.sex = 'y';
-
//const 修饰的变量只能用const修饰的变量来接收
-
t1.p = str;
-
t2=t1;
-
printf("t2: %d,%c,%s\n",t2.id,t2.sex,t2.p);
-
//此处打印出来的丢值都是一样的,说明并没有将内容复制一块给新指针,
-
//只是让新的指针指向原来的那个地址,这就相当于,指针在这个过程中只赋值了地值
-
//而不是内容
-
printf("s1 ptr:%p,p2 ptr:%p\n",t1.p,t2.p);
-
//这里没有进行运算符重载,所以会出现operator+();
-
cout<<"t2:"<<t2.id<<t2.sex,t2.p<<endl;
-
cout<<"t1.p:"<<(void *)t1.p<<"t2.p"<<(void *)t2.p<<endl;
-
return 0;
-
}
-
*/
//上面函数的原理:
/*在拷贝过程中,如果没有自定义拷贝构造函数,系统会提供一个缺省的拷贝构造函数
缺省的拷贝构造函数对基本类型的成员变量,按字节赋值,对于类型的成员变量,调用其相应类型的拷贝构造函数
。但是注意缺省的构造函数确实这样的:缺省拷贝构造函数在拷贝过程中是按自己复制的,对于指针变量只赋值指针本身,
而不赋值指针所指项目标======浅拷贝
这就是问题产生的原因: ***********浅拷贝出现了****************************************************
在对对象进行赋值之后,事实上S1,S2的成员都指向同一块内存空间(内存共享)在t1析构时,delete了成员指针所指向的内存空间
而t2被析构时同样指向了(此时这个指针已经变成了野指针)并且要释放这篇已经被t1析构了的内存空间。在同一片空间出现两次释放
自然会出现段错误;
由于内存共享,只要一个子对象改变了其中的值,另一个对象的值也就改变了。
怎么实现深拷贝:
自定义拷贝构造函数
*/
-
#include <iostream>
-
#include <stdio.h>
-
#include <string.h>
-
using namespace std;
-
struct Teacher
-
{
-
int id;
-
char sex;
-
char *p;
-
//自定义拷贝构造函数,实现深拷贝
-
Teacher operator=(Teacher & tec)
-
{
-
id = tec.id;
-
sex = tec.sex;
-
p = new char(strlen(tec.p)+1);
-
strcpy(this->p,tec.p);
-
return *this;
-
}
-
};
-
//相当于重载operator= 方法,这样还是运行,结果就变了
-
int main()
-
{
-
struct Teacher t1, t2;
-
//const 修饰加上,因为const 存储在常量区,不能被修改,通过指针修改比较危险
-
//所以通过前面加上const之后,警告消失
-
char *str ="i am string";
-
t1.id = 345;
-
t1.sex = 'f';
-
//const 修饰的变量只能用const修饰的变量来接收
-
t1.p = (char *)str;
-
t2=t1;
-
printf("t2: %d,%c,%s\n",t2.id,t2.sex,t2.p);
-
printf("s1 ptr stress:%p,p2 ptr stress:%p\n",t1.p,t2.p);
-
printf("s1 ptr:%s,p2 ptr:%s\n",t1.p,t2.p);
-
return 0;
-
}
//类和上面的结构体一致,其实可以将结构体看成一个类来处理,结构体可以有自己的构造,析构,重载运算符,
//可以简单的认为结构体是类的一种形式。
//拷贝函数有两种:深拷贝 浅拷贝
//当出现类的等号赋值时,会调用拷贝函数,在未定义的显示拷贝构造函数的情况下,系统会调用默认的拷贝函数----浅拷贝
//能够完成成员的复制
//当成员数据中没有指针时,浅拷贝是可以的,但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针指向同一个地址,
//当对象快结束的时候,会调用两次析构,而导致指针悬挂现象。所以,这时必须采用深拷贝,深拷贝与前拷贝的不同在于
//深拷贝会在堆内存中申请空间来存储数据,从而也就结局了指针悬挂的问题。简而言之,当数据成员中有指针的时候,必须要用
//深拷贝。
//建议:
// 我们在定义类或者是结构体的时候,最后都重写拷贝构造函数,避免浅拷贝;这类不宜发现但后果严重的错误产生。