1. 深拷贝和浅拷贝(拷贝构造函数的使用)
有时候需要自己定义拷贝构造函数,以避免浅拷贝问题。
在什么情况下需要用户自己定义拷贝构造函数:
一般情况下,当类中成员有指针变量、类中有动态内存分配时常常需要用户自己定义拷贝构造函数。
在什么情况下系统会调用拷贝构造函数:(三种情况)
(1)用类的一个对象去初始化另一个对象时
(2)当函数的形参是类的对象时(也就是值传递时),如果是引用传递则不会调用
(3)当函数的返回值是类的对象或引用时
简单示例:
-
#include <iostream> -
using namespace std; -
-
class A -
{ -
private: -
int a; -
public: -
A(int i){a=i;} //内联的构造函数 -
A(A &aa); -
int geta(){return a;} -
}; -
-
A::A(A &aa) //拷贝构造函数 -
{ -
a=aa.a; -
cout<<”拷贝构造函数执行!”<<endl; -
} -
-
int get_a(A aa) //参数是对象,是值传递,会调用拷贝构造函数 -
{ -
return aa.geta(); -
} -
-
int get_a_1(A &aa) //如果参数是引用类型,本身就是引用传递,所以不会调用拷贝构造函数 -
{ -
return aa.geta(); -
} -
-
A get_A() //返回值是对象类型,会调用拷贝构造函数。会调用拷贝构造函数,因为函数体内生成的对象aa是临时的,离开这个函数就消失了。所有会调用拷贝构造函数复制一份。 -
{ -
A aa(1); -
return aa; -
} -
-
A& get_A_1() //会调用拷贝构造函数,因为函数体内生成的对象aa是临时的,离开这个函数就消失了。所有会调用拷贝构造函数复制一份。 -
{ -
A aa(1); -
return aa; -
} -
-
int _tmain(int argc, _TCHAR* argv[]) -
{ -
A a1(1); -
A b1(a1); //用a1初始化b1,调用拷贝构造函数 -
A c1=a1; //用a1初始化c1,调用拷贝构造函数 -
-
int i=get_a(a1); //函数形参是类的对象,调用拷贝构造函数 -
int j=get_a_1(a1); //函数形参类型是引用,不调用拷贝构造函数 -
-
A d1=get_A(); //调用拷贝构造函数 -
A e1=get_A_1(); //调用拷贝构造函数 -
-
return 0; -
}
附:一个面试试题
修改下面程序中的错误:
-
#include <iostream> -
using namespace std; -
-
class NameStr -
{ -
private: -
char *m_pName; -
char *m_pData; -
public: -
NameStr() -
{ -
static const char s_szDefaultName[]=“Default name”; -
static const char s_szDefaultStr[]=“Default string”; -
strcpy(m_pName,s_szDefaultName); -
strcpy(m_pData,s_szDefaultStr); -
} -
~NamedStr(){} -
NameStr(const char pName,const char pData) -
{ -
m_pData=new char[strlen(pData)]; -
m_pName=new char[strlen(pData)]; -
} -
-
void Print() -
{ -
cout<<”Name:”<<m_pName<<endl; -
cout<<”String:”<<m_pData<<endl; -
} -
}; -
-
int _tmain(int argc, _TCHAR* argv[]) -
{ -
NameStr* pDefNss=NULL; -
-
pDefNss=new NameStr[10]; -
NameStr ns(”hello”,“world”); -
-
delete pDefNss; -
-
return 0; -
}
分析:
1. 第14、15行,strcpy(m_pName,s_szDefaultName) 对未分配内存空间的字符指针赋值会出现异常。
2. 第20行、21行,m_pData=new char[strlen(pData)] 应该为m_pData=new char[strlen(pData)+1] ,并且应该为最后一个字符赋值为’\0’。
3. 析构函数中,应该处理字符指针内存空间的释放。
4. 因为类的成员变量中有指针变量,因此应该编写类的拷贝构造函数和赋值函数,防止浅拷贝。
5. pDefNss是一个对象数组,delete时应该是delete [ ]pDefNss。
比较规范的代码如下:
-
#include <iostream> -
using namespace std; -
-
//NameStr类的声明 -
class NameStr -
{ -
private: -
char *m_pName; -
char *m_pData; -
public: -
NameStr(); //默认拷贝构造函数 -
-
~NameStr(); //析构函数声明 -
-
NameStr(const char pName,const char pData); //带参构造函数的声明 -
-
NameStr(const NameStr& temp); //拷贝构造函数的声明 -
-
NameStr& operator= (const NameStr& temp); //重载=运算符 -
-
void Print(); //输出对象内容 -
}; -
-
//默认构造函数的实现 -
NameStr::NameStr() -
{ -
static const char s_szDefaultName[]=“Default name”; -
static const char s_szDefaultStr[]=“Default string”; -
-
m_pData=new char[strlen(s_szDefaultStr)+1]; //不能为为分配内存空间的字符指针赋值 -
m_pName=new char[strlen(s_szDefaultName)+1]; -
-
strcpy(m_pName,s_szDefaultName); //更规范的方式是使用strncpy函数进行拷贝 -
m_pName[strlen(s_szDefaultName)]=’\0’; -
strcpy(m_pData,s_szDefaultStr); -
m_pData[strlen(s_szDefaultStr)]=’\0’; -
} -
-
//析构函数的实现 -
NameStr::~NameStr() -
{ -
delete []m_pData; -
delete []m_pName; -
} -
-
//带参构造函数的实现 -
NameStr::NameStr(const char pName,const char pData) -
{ -
m_pData=new char[strlen(pData)+1]; //开辟内存空间 -
m_pName=new char[strlen(pName)+1]; -
-
strcpy(m_pData,pData); -
m_pData[strlen(pData)]=’\0’; -
strcpy(m_pName,pName); -
m_pName[strlen(pName)]=’\0’; -
} -
-
//拷贝构造函数的实现 -
NameStr::NameStr(const NameStr& temp) -
{ -
m_pData=new char[strlen(temp.m_pData)+1]; -
m_pName=new char[strlen(temp.m_pName)+1]; -
-
strcpy(m_pData,temp.m_pData); -
m_pData[strlen(temp.m_pData)]=’\0’; -
strcpy(m_pName,temp.m_pName); -
m_pName[strlen(temp.m_pName)]=’\0’; -
} -
-
//重载=运算符的实现 -
NameStr& NameStr::operator=(const NameStr& temp) -
{ -
//首先要进行检查,防止自身复制 -
if(&temp==this) //this是一个指针,表示本对象的地址。&temp是temp对象的指针。 -
{ -
return *this; -
} -
-
//释放原有的内存空间 -
delete []m_pData; -
delete []m_pName; -
-
//分配新的内存空间 -
m_pData=new char[strlen(temp.m_pData)+1]; -
m_pName=new char[strlen(temp.m_pName)+1]; -
-
//进行拷贝 -
strcpy(m_pData,temp.m_pData); -
m_pData[strlen(temp.m_pData)]=’\0’; -
strcpy(m_pName,temp.m_pName); -
m_pName[strlen(temp.m_pName)]=’\0’; -
-
//返回本对象的引用 -
return *this; -
} -
-
inline void NameStr::Print() -
{ -
cout<<”Name:”<<m_pName<<endl; -
cout<<”String:”<<m_pData<<endl; -
} -
-
//程序入口 -
int _tmain(int argc, _TCHAR* argv[]) -
{ -
NameStr* pDefNss=NULL; -
-
pDefNss=new NameStr[3]; -
NameStr ns(”hello”,“world”); -
-
delete []pDefNss; -
-
NameStr ns1=ns; -
-
return 0; -
}
转自: http://blog.csdn.net/hyhyl1990/article/details/7957604
1. 深拷贝和浅拷贝(拷贝构造函数的使用)
有时候需要自己定义拷贝构造函数,以避免浅拷贝问题。
在什么情况下需要用户自己定义拷贝构造函数:
一般情况下,当类中成员有指针变量、类中有动态内存分配时常常需要用户自己定义拷贝构造函数。
在什么情况下系统会调用拷贝构造函数:(三种情况)
(1)用类的一个对象去初始化另一个对象时
(2)当函数的形参是类的对象时(也就是值传递时),如果是引用传递则不会调用
(3)当函数的返回值是类的对象或引用时
简单示例:
-
#include <iostream> -
using namespace std; -
-
class A -
{ -
private: -
int a; -
public: -
A(int i){a=i;} //内联的构造函数 -
A(A &aa); -
int geta(){return a;} -
}; -
-
A::A(A &aa) //拷贝构造函数 -
{ -
a=aa.a; -
cout<<”拷贝构造函数执行!”<<endl; -
} -
-
int get_a(A aa) //参数是对象,是值传递,会调用拷贝构造函数 -
{ -
return aa.geta(); -
} -
-
int get_a_1(A &aa) //如果参数是引用类型,本身就是引用传递,所以不会调用拷贝构造函数 -
{ -
return aa.geta(); -
} -
-
A get_A() //返回值是对象类型,会调用拷贝构造函数。会调用拷贝构造函数,因为函数体内生成的对象aa是临时的,离开这个函数就消失了。所有会调用拷贝构造函数复制一份。 -
{ -
A aa(1); -
return aa; -
} -
-
A& get_A_1() //会调用拷贝构造函数,因为函数体内生成的对象aa是临时的,离开这个函数就消失了。所有会调用拷贝构造函数复制一份。 -
{ -
A aa(1); -
return aa; -
} -
-
int _tmain(int argc, _TCHAR* argv[]) -
{ -
A a1(1); -
A b1(a1); //用a1初始化b1,调用拷贝构造函数 -
A c1=a1; //用a1初始化c1,调用拷贝构造函数 -
-
int i=get_a(a1); //函数形参是类的对象,调用拷贝构造函数 -
int j=get_a_1(a1); //函数形参类型是引用,不调用拷贝构造函数 -
-
A d1=get_A(); //调用拷贝构造函数 -
A e1=get_A_1(); //调用拷贝构造函数 -
-
return 0; -
}
附:一个面试试题
修改下面程序中的错误:
-
#include <iostream> -
using namespace std; -
-
class NameStr -
{ -
private: -
char *m_pName; -
char *m_pData; -
public: -
NameStr() -
{ -
static const char s_szDefaultName[]=“Default name”; -
static const char s_szDefaultStr[]=“Default string”; -
strcpy(m_pName,s_szDefaultName); -
strcpy(m_pData,s_szDefaultStr); -
} -
~NamedStr(){} -
NameStr(const char pName,const char pData) -
{ -
m_pData=new char[strlen(pData)]; -
m_pName=new char[strlen(pData)]; -
} -
-
void Print() -
{ -
cout<<”Name:”<<m_pName<<endl; -
cout<<”String:”<<m_pData<<endl; -
} -
}; -
-
int _tmain(int argc, _TCHAR* argv[]) -
{ -
NameStr* pDefNss=NULL; -
-
pDefNss=new NameStr[10]; -
NameStr ns(”hello”,“world”); -
-
delete pDefNss; -
-
return 0; -
}
分析:
1. 第14、15行,strcpy(m_pName,s_szDefaultName) 对未分配内存空间的字符指针赋值会出现异常。
2. 第20行、21行,m_pData=new char[strlen(pData)] 应该为m_pData=new char[strlen(pData)+1] ,并且应该为最后一个字符赋值为’\0’。
3. 析构函数中,应该处理字符指针内存空间的释放。
4. 因为类的成员变量中有指针变量,因此应该编写类的拷贝构造函数和赋值函数,防止浅拷贝。
5. pDefNss是一个对象数组,delete时应该是delete [ ]pDefNss。
比较规范的代码如下:
-
#include <iostream> -
using namespace std; -
-
//NameStr类的声明 -
class NameStr -
{ -
private: -
char *m_pName; -
char *m_pData; -
public: -
NameStr(); //默认拷贝构造函数 -
-
~NameStr(); //析构函数声明 -
-
NameStr(const char pName,const char pData); //带参构造函数的声明 -
-
NameStr(const NameStr& temp); //拷贝构造函数的声明 -
-
NameStr& operator= (const NameStr& temp); //重载=运算符 -
-
void Print(); //输出对象内容 -
}; -
-
//默认构造函数的实现 -
NameStr::NameStr() -
{ -
static const char s_szDefaultName[]=“Default name”; -
static const char s_szDefaultStr[]=“Default string”; -
-
m_pData=new char[strlen(s_szDefaultStr)+1]; //不能为为分配内存空间的字符指针赋值 -
m_pName=new char[strlen(s_szDefaultName)+1]; -
-
strcpy(m_pName,s_szDefaultName); //更规范的方式是使用strncpy函数进行拷贝 -
m_pName[strlen(s_szDefaultName)]=’\0’; -
strcpy(m_pData,s_szDefaultStr); -
m_pData[strlen(s_szDefaultStr)]=’\0’; -
} -
-
//析构函数的实现 -
NameStr::~NameStr() -
{ -
delete []m_pData; -
delete []m_pName; -
} -
-
//带参构造函数的实现 -
NameStr::NameStr(const char pName,const char pData) -
{ -
m_pData=new char[strlen(pData)+1]; //开辟内存空间 -
m_pName=new char[strlen(pName)+1]; -
-
strcpy(m_pData,pData); -
m_pData[strlen(pData)]=’\0’; -
strcpy(m_pName,pName); -
m_pName[strlen(pName)]=’\0’; -
} -
-
//拷贝构造函数的实现 -
NameStr::NameStr(const NameStr& temp) -
{ -
m_pData=new char[strlen(temp.m_pData)+1]; -
m_pName=new char[strlen(temp.m_pName)+1]; -
-
strcpy(m_pData,temp.m_pData); -
m_pData[strlen(temp.m_pData)]=’\0’; -
strcpy(m_pName,temp.m_pName); -
m_pName[strlen(temp.m_pName)]=’\0’; -
} -
-
//重载=运算符的实现 -
NameStr& NameStr::operator=(const NameStr& temp) -
{ -
//首先要进行检查,防止自身复制 -
if(&temp==this) //this是一个指针,表示本对象的地址。&temp是temp对象的指针。 -
{ -
return *this; -
} -
-
//释放原有的内存空间 -
delete []m_pData; -
delete []m_pName; -
-
//分配新的内存空间 -
m_pData=new char[strlen(temp.m_pData)+1]; -
m_pName=new char[strlen(temp.m_pName)+1]; -
-
//进行拷贝 -
strcpy(m_pData,temp.m_pData); -
m_pData[strlen(temp.m_pData)]=’\0’; -
strcpy(m_pName,temp.m_pName); -
m_pName[strlen(temp.m_pName)]=’\0’; -
-
//返回本对象的引用 -
return *this; -
} -
-
inline void NameStr::Print() -
{ -
cout<<”Name:”<<m_pName<<endl; -
cout<<”String:”<<m_pData<<endl; -
} -
-
//程序入口 -
int _tmain(int argc, _TCHAR* argv[]) -
{ -
NameStr* pDefNss=NULL; -
-
pDefNss=new NameStr[3]; -
NameStr ns(”hello”,“world”); -
-
delete []pDefNss; -
-
NameStr ns1=ns; -
-
return 0; -
}
转自: http://blog.csdn.net/hyhyl1990/article/details/7957604