1.1 拷贝构造函
c++通过拷贝构造函数实现对象拷贝.所以先介绍一下拷贝构造函数.
实例:
CA A(100,"123")//构造函数赋值
CExample B=A//拷贝构造函数赋值
CExample C(A)//拷贝构造函数赋值
CExample DD=A//赋值构造函数赋值
g_fun(A)//传值拷贝调用对象构造函数
拷贝构造函数实现分析:
调用g_Fun()时,会产生以下几个重要步骤:
(1).函数调用传值拷贝,会先会产生一个临时变量,就叫 C 吧。
(2).然后调用拷贝构造函数把A的值给C.整个这两个步骤有点像:CA C(A)
(3).等g_Fun()执行完后, 析构掉 C 对象。C对象完成了在g_Fun()函数内部的工作.
1.2 浅拷贝
浅拷贝只拷贝基本数据类型(非指针变量).
对于指针变量,对象B的指针变量会 指向 对象A的指针变量内存,不会拷贝.
类缺省拷贝构造函数是浅拷贝.上例中的拷贝构造函数的实现就是浅拷贝.
浅拷贝的问题是如果对象中变量带有指针,则会发生错误.因为两个指针指向同一个内存,一个对象修改,另一个对象的值也被更改了.
当在析构的时候,会发生两次free同一个内存,造成错误.
下面的link介绍了浅拷贝和c的简单神拷贝.c++深拷贝参见1.3节,c的高级深拷贝参见2.2节。
参见 https://blog.csdn.net/cyy_0802/article/details/80374812
1.3 c++深拷贝
在拷贝构造函数中分配内存,将入参对象的指针变量指向的内存,全部拷贝一份就是深拷贝。
实例分析
CA A(10,"Hello!")//构造函数初始化对象。
CA B=A//拷贝构造函数.
上例将str的内容拷贝一份,实现了深拷贝.
扩展问题,如果类中变量不是char* str,而是另一个对象的指针会怎么样?
2.1 浅拷贝
和上面的c++浅拷贝一样. 两个指针指向同一个内存.
2.2 深拷贝
(1) 如果struct中没有指针变量
直接拷贝内存即可.
(2) 如果struct中带有指针变量
需要自己实现拷贝函数,将每个item拷贝一份.(实现类似c++的拷贝构造函数)
扩展情况:如果将char *data换成struct S_NodeA *nodeA会怎么样呢?
CopyStruct将非struct的item拷贝一份。nodeA需要嵌套拷贝.调用拷贝S_Node的函数CopyNode完成拷贝.
总结:浅拷贝就是指针赋值,不分配内存,两个指针指向一个内存. 深拷贝就是拷贝指针指向的内存.如果有嵌套对象的话,嵌套拷贝.
c++11 使用移动构造函数实现深拷贝
移动构造函数的原理是,指针A和B同时指向一块内存,然后将原来的A指针置NULL. 这样避免了两个指针指向同一个内存,也避免了内存拷贝.
构造函数构造函数 是一种特殊的方法 主要用来在创建对象时初始化对象 即为对象成员变量赋初始值
总与new运算符一起使用在创建对象的语句中 特别的一个类可以有多个构造函数 可根据其参数个数的不同或参数类型的不同来区分它们 即构造函数的重载
构造函数与其他方法的区别
1.构造函数的命名必须和类名完全相同而一般方法则不能和类名相同.
2.构造函数的功能主要用于在类的对象创建时定义初始化的状态.它没有返回值,也不能用void来修饰.这就保证了它不仅什么也不用自动返回,而且根本不能有任何选择.而其他方法都有返回值.即使是void返回值,尽管方法体本身不会自动返回什么,但仍然可以让它返回一些东西,而这些东西可能是不安全的.
3.构造函数不能被直接调用,必须通过new运算符在创建对象时才会自动调用,一般方法在程序执行到它的时候被调用.
4.当定义一个类定义的时候,通常情况下都会显示该类的构造函数,并在函数中指定初始化的工作也可省略不去Java编译器会提供一个默认的构造函数.此默认构造函数是不带参数的.而一般方法不存在这一特点
拷贝构造函数
拷贝构造函数,经常被称作X(X&),是一种特殊的构造函数,他由编译器调用来完成一些基于同一类的其他对象的构件及初始化。它的唯一的一个参数(对象的引用)是不可变的(因为是const型的)。这个函数经常用在函数调用期间于用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。
在C++中,下面三种对象需要拷贝的情况。因此,拷贝构造函数将会被调用。
1). 一个对象以值传递的方式传入函数体
2). 一个对象以值传递的方式从函数返回
3). 一个对象需要通过另外一个对象进行初始化
以上的情况需要拷贝构造函数的调用。如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值 *** 作赋共同实现的。描述拷贝构造函数和赋值运算符的异同的参考资料有很多。
拷贝构造函数不可以改变它所引用的对象,其原因如下:当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动的被调用来生成函数中的对象。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环。
除了当对象传入函数的时候被隐式调用以外,拷贝构造函数在对象被函数返回的时候也同样的被调用。换句话说,你从函数返回得到的只是对象的一份拷贝。但是同样的,拷贝构造函数被正确的调用了,你不必担心。
如果在类中没有显式的声明一个拷贝构造函数,那么,编译器会私下里为你制定一个函数来进行对象之间的位拷贝(bitwise copy)。这个隐含的拷贝构造函数简单的关联了所有的类成员。许多作者都会提及这个默认的拷贝构造函数。注意到这个隐式的拷贝构造函数和显式声明的拷贝构造函数的不同在于对于成员的关联方式。显式声明的拷贝构造函数关联的只是被实例化的类成员的缺省构造函数除非另外一个构造函数在类初始化或者在构造列表的时候被调用。
拷贝构造函数是程序更加有效率,因为它不用再构造一个对象的时候改变构造函数的参数列表。设计拷贝构造函数是一个良好的风格,即使是编译系统提供的帮助你申请内存默认拷贝构造函数。事实上,默认拷贝构造函数可以应付许多情况。
以下讨论中将用到的例子:
class CExample
{
public:
CExample()
~CExample()
void Init(int n)
private:
char *pBuffer//类的对象中包含指针,指向动态分配的内存资源
int nSize
}
这个类的主要特点是包含指向其他资源的指针。
pBuffer指向堆中分配的一段内存空间。
一、拷贝构造函数
int main(int argc, char* argv[])
{
CExample theObjone
theObjone.Init40)
//现在需要另一个对象,需要将他初始化称对象一的状态
CExample theObjtwo=theObjone
...
}
语句"CExample theObjtwo=theObjone"用theObjone初始化theObjtwo。
其完成方式是内存拷贝,复制所有成员的值。
完成后,theObjtwo.pBuffer==theObjone.pBuffer。
即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了。这样不符合要求,对象之间不独立了,并为空间的删除带来隐患。
所以需要采用必要的手段来避免此类情况。
回顾以下此语句的具体过程:首先建立对象theObjtwo,并调用其构造函数,然后成员被拷贝。
可以在构造函数中添加 *** 作来解决指针成员的问题。
所以C++语法中除了提供缺省形式的构造函数外,还规范了另一种特殊的构造函数:拷贝构造函数,上面的语句中,如果类中定义了拷贝构造函数,这对象建立时,调用的将是拷贝构造函数,在拷贝构造函数中,可以根据传入的变量,复制指针所指向的资源。
拷贝构造函数的格式为:构造函数名(对象的引用)
提供了拷贝构造函数后的CExample类定义为:
class CExample
{
public:
CExample()
~CExample()
CExample(const CExample&)//拷贝构造函数
void Init(int n)
private:
char *pBuffer//类的对象中包含指针,指向动态分配的内存资源
int nSize
}
CExample::CExample(const CExample&RightSides) //拷贝构造函数的定义
{
nSize=RightSides.nSize//复制常规成员
pBuffer=new char[nSize]//复制指针指向的内容
memcpy(pBuffer,RightSides.pBuffer,nSize*sizeof(char))
}
这样,定义新对象,并用已有对象初始化新对象时,CExample(const CExample&RightSides)将被调用,而已有对象用别名RightSides传给构造函数,以用来作复制。
原则上,应该为所有包含动态分配成员的类都提供拷贝构造函数。
拷贝构造函数的另一种调用。
当对象直接作为参数传给函数时,函数将建立对象的临时拷贝,这个拷贝过程也将调同拷贝构造函数。
例如
BOOL testfunc(CExample obj)
testfunc(theObjone)//对象直接作为参数。
BOOL testfunc(CExample obj)
{
//针对obj的 *** 作实际上是针对复制后的临时拷贝进行的
}
还有一种情况,也是与临时对象有关的
当函数中的局部对象被被返回给函数调者时,也将建立此局部对象的一个临时拷贝,拷贝构造函数也将被调用
CTest func()
{
CTest theTest
return theTest
}
二、赋值符的重载
下面的代码与上例相似
int main(int argc, char* argv[])
{
CExample theObjone
theObjone.Init(40)
CExample theObjthree
theObjthree.Init(60)
//现在需要一个对象赋值 *** 作,被赋值对象的原内容被清除,并用右边对象的内容填充。
theObjthree=theObjone
return 0
}
也用到了"="号,但与"一、"中的例子并不同,"一、"的例子中,"="在对象声明语句中,表示初始化。更多时候,这种初始化也可用括号表示。
例如 CExample theObjone(theObjtwo)
而本例子中,"="表示赋值 *** 作。将对象theObjone的内容复制到对象theObjthree,这其中涉及到对象theObjthree原有内容的丢弃,新内容的复制。
但"="的缺省 *** 作只是将成员变量的值相应复制。旧的值被自然丢弃。
由于对象内包含指针,将造成不良后果:指针的值被丢弃了,但指针指向的内容并未释放。指针的值被复制了,但指针所指内容并未复制。
因此,包含动态分配成员的类除提供拷贝构造函数外,还应该考虑重载"="赋值 *** 作符号。
类定义变为:
class CExample
{
...
CExample(const CExample&)//拷贝构造函数
CExample&operator = (const CExample&)//赋值符重载
...
}
//赋值 *** 作符重载
CExample &CExample::operator = (const CExample&RightSides)
{
nSize=RightSides.nSize//复制常规成员
char *temp=new char[nSize]//复制指针指向的内容
memcpy(temp,RightSides.pBuffer,nSize*sizeof(char))
delete []pBuffer//删除原指针指向内容 (将删除 *** 作放在后面,避免X=X特殊情况下,内容的丢失)
pBuffer=NULL
pBuffer=temp //建立新指向
return *this
}
三、拷贝构造函数使用赋值运算符重载的代码。
CExample::CExample(const CExample&RightSides)
{
*this=RightSides //调用重载后的"="
}
分类: 电脑/网络 >>程序设计 >>其他编程语言问题描述:
设计一个用语人事管理的“人员”类。由于考虑通用性,这里只抽象出所有类型人员都具有的属性:编号,性别,出生日期,身份z号等。其中“出生日期”声明为一个“日期”类内嵌对象。用成员函数实现对人员信息的录入和显示。要求包括:构造函数和析构函数,拷贝构造函数,内联成员函数,带默认形参值的成员函数,类的组合。
我写了一下,就是人员类的构造函数和拷贝构造函数,写不好,如果不写的话我就可以运行,请高手帮忙补上构造函数,小弟谢谢了,要多少分,自己说(只要我给的起)。
#include<iostream>
using namespace std
class date 出生日期
{
public:
date(int y=1980,int m=1,int d=1){year=ymonth=mday=d}
date(date &p)
~ date(){}
void setdate()
void outputdate()
private:
int year
int month
int day
}
void date::setdate()
{
int newy
int newm
int newd
cout<<"请输入出生 年 月 日 :"
cin>>newy>>newm>>newd
year=newy
month=newm
day=newd
}
void date::outputdate ()
{
cout<<year<<"年"<<month<<"月"<<day<<"日"<<endl
}
class person人员类
{
public:
void setinformation()
void showinformation()
private:
long number
char sex
date c
long idno
}
void person::setinformation()
{
long num
char s
long id
cout<<"请输入员工信息 :"<<"1 编号:"<<endl
cin>>num
cout<<"2 性别(男 M,女 F)"<<endl
cin>>s
cout<<"3 身份z号"<<endl
cin>>id
number=num
sex=s
c.setdate ()
idno=id
}
void person::showinformation()
{
cout<<"编号:"<<number<<endl
cout<<"性别:"<<sex<<endl
cout<<"出生日期"
c.outputdate()
cout<<"身份z号"<<idno<<endl
}
int main()
{person m1
person m2
m1.setinformation()
m1.showinformation()
m2.showinformation ()
}
解析:
估计能满足你
class CDate
{
private:
int m_nYear
int m_nMonth
int m_nDay
public:
CDate(int y=1985,int m=12,int d=13)
void Modify(int ye,int mo,int da)
void disp() const
}
CDate::CDate(int y/* =1985 */,int m/* =12 */,int d/* =13 */)
{
m_nYear=y
m_nMonth=m
m_nDay=d
}
void CDate::Modify(int ye,int mo,int da)
{
m_nYear=ye
m_nMonth=mo
m_nDay=da
}
void CDate::disp() const
{
cout<<m_nYear<<"年"<<m_nMonth<<"月"<<m_nDay<<"日"<<endl
}
class CStaff
{
private:
int m_nNum
char m_bSex
double m_dID
CDate m_BirthDay
public:
CStaff()默认构造函数
CStaff(int n,char s,double i, CDate d)
CStaff(const CStaff&staff)拷贝构造函数
~CStaff()析构函数
void show() const
}
CStaff::CStaff()
{
cout<<"构造函数!"<<endl
}
CStaff::CStaff(int n, char s,double i,CDate d)
{
m_nNum=n
m_bSex=s
m_BirthDay=d
m_dID=i
}
CStaff::CStaff(const CStaff&staff)
{
m_nNum=staff.m_nNum
m_bSex=staff.m_bSex
m_BirthDay=staff.m_BirthDay
m_dID=staff.
}
CStaff::~CStaff()
{
cout<<"析构函数!"<<endl
}
void CStaff::show()const
{
cout<<"编号:"<<m_nNum<<endl
cout<<"性别:"<<m_bSex<<endl
cout<<"出生年月:"<<endl
m_BirthDay.disp()
cout<<"身份z号:"<<m_dID<<endl
}
void main()
{
int y,m,d,no
char sex
double ID
CDate test
cout<<"请输入编号,性别,身份z号:\n"
cin>>no>>sex>>ID需要:using namespace std,否则有错!!!
cout<<"请输入出生年月日:\n"
cin>>y>>m>>d
test.Modify(y,m,d)
CStaff staff(no,sex,ID,test)
staff.show()
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)