十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
Test c(Test());//这是函数声明,等价于Test c(Test(*func)());即接受一个函数指针,返回Test对象的函数
我们提供的服务有:成都网站建设、成都网站设计、微信公众号开发、网站优化、网站认证、雨山ssl等。为近1000家企事业单位解决了网站和推广的问题。提供周到的售前咨询和贴心的售后服务,是有科学管理、有技术的雨山网站制作公司
Test d(Test const());//这也只是函数声明而已,大致上和上面一个声明同义
前段时间被这种恶心的解析规则坑过,教训就是不要画蛇添足地写上Test(),Test c就能完成需要的效果
拷贝构造是构造函数的一种.
它的原理是函数的重载.
选择参数类型一样的函数进行操作.
如果有两个参数一样的(拷贝)构造函数,它就会出错,不知道为选择那一个.
void main()
{
Point a(4,2) //调用构造函数 //正确
Point b(a); //调用复制构造函数,不调用构造函数 //正确
Point c(7,8); //调用构造函数 //正确
}
class CStr
{
public:
CStr(); //默认构造函数
CStr(const char* psz); //一种广义拷贝构造函数
CStr(const CStr str); //拷贝构造函数
const CStr operator=(const CStr str); //赋值构造函数
size_t GetSize() const; //这里的const是什么意思?它必须吗?
operator const char*() const { return m_pdata; }
protected:
const CStr* _Copy(const CStr str);
private:
char* m_pdata;
size_t m_size;
};
CStr::CStr()
{
m_pdata = NULL;
m_size = 0;
}
size_t CStr::GetSize() const
{
return m_size;
}
const CStr* CStr::_Copy(const CStr str)
{
if(this != str)
{
if(m_pdata){
delete[] m_pdata;
}
m_size = str.GetSize();
m_pdata = new char[m_size + 1]; assert(m_pdata);
strcpy(m_pdata, str);
}
return this;
}
CStr::CStr(const char* psz) : m_pdata(NULL), m_size(0)
{
assert(psz);
if(m_pdata != psz)
{
if(m_pdata){
delete[] m_pdata;
}
m_size = strlen(psz);
m_pdata = new char[m_size + 1]; assert(m_pdata);
strcpy(m_pdata, psz);
}
}
CStr::CStr(const CStr str): m_pdata(NULL), m_size(0)
{
_Copy(str);
}
const CStr CStr::operator=(const CStr str)
{
return *(_Copy(str));
}
int main()
{
const char* psz = "test";
const char* psz1 = "me";
CStr str(psz); //拷贝构造函数,此处调用的是CStr(const char* psz)。 #1
CStr str2(str); // 拷贝构造函数,此处调用的是 CStr(const CStr str) #2
CStr str1 = psz1; // 拷贝构造,str1此前并不存在,现在要先构造它。 #3
str = str1; // 真正的赋值构造函数 #4
return 0;
}
上面这个小型的例子,主要就是为了阐述赋值构造函数与拷贝构造函数之间的区别,其实,赋值构造函数不应该被称为一个构造函数,充其量只能算得上一个重载的操作符函数。
在C语言中,我们知道"=="是判等操作符,而"="却是一个赋值操作符,所以只要没有出现"="操作符,也就不会有赋值操作可言。
在调用赋值构造函数的时候,类对象已经存在,自然谈不上构造类对象,它只是对该已经存在的类对象的成员变量进行修改(所谓的赋值)操作。而拷贝构造函数是利用一个已经存在的类对象构造出一个新的类对象。
回到上面的例子,相信大多数人都应该能分清#1、#2、#4所调用的函数,而对于#3,赋值构造的实际用意是修改一个已有的对象,而现在str1还没有被定义,所以此处还是必须要调用拷贝构造函数先产生一个CStr对象。
最后,定义与赋值同时进行的话,必须要先定义,所以必须得先调用拷贝构造函数,而对于符合单纯的赋值操作语法的语句(也就是只有两个操作数的表达式)来说,才是真正的调用赋值构造函数。
构造函数:是用于对对象初始化名字,必须与类的名字相同,比如说定义一个圆类。
复制构造函数,在某种意义上相当于运算符“=”重载,把一个对象的数值复制给另一个
class circle
{
public:
circle (int r):radium(r){};//构造函数的第一种写法
circle (int r){radium=r;}//构造函数的第二种写法
circle(circle c)
{
radium=c.radium;//这样复制,在主函数里用cir1(cir2);这样使用
}
private:
int radium;
};
拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的参数(对象的引用)是不可变的(const类型)。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。
目录
拷贝构造函数说明
一、拷贝函数
二、值的重载
三、注意事宜
四、拷贝构造与构造区别
展开
编辑本段
拷贝构造函数说明
调用拷贝构造函数的情形
在C++中,下面三种对象需要调用拷贝构造函数:
1) 一个对象以值传递的方式传入函数体;
2) 一个对象以值传递的方式从函数返回;
3) 一个对象需要通过另外一个对象进行初始化;
如果在前两种情况不使用拷贝构造函数的时候,就会导致一个指针指向已经被删除的内存空间。对于第三种情况来说,初始化和赋值的不同含义是构造函数调用的原因。事实上,拷贝构造函数是由普通构造函数和赋值操作符共同实现的。描述拷贝构造函数和赋值运算符的异同的参考资料有很多。
拷贝构造函数不可以改变它所引用的对象,其原因如下:当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动的被调用来生成函数中的对象。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环直至栈溢出(Stack Overflow)。除了当对象传入函数的时候被隐式调用以外,拷贝构造函数在对象被函数返回的时候也同样的被调用。
隐式的拷贝构造函数
如果在类中没有显式的声明一个拷贝构造函数,那么,编译器会自动生成一个来进行对象之间的位拷贝(Bitwise Copy)。这个隐含的拷贝构造函数简单的关联了所有的类成员。注意到这个隐式的拷贝构造函数和显式声明的拷贝构造函数的不同在于对成员的关联方式。显式声明的拷贝构造函数关联的只是被实例化的类成员的缺省构造函数,除非另外一个构造函数在类初始化或构造列表的时候被调用。
拷贝构造函数使程序更有效率,因为它不用再构造一个对象的时候改变构造函数的参数列表。设计拷贝构造函数是一个良好的风格,即使是编译系统会自动为你生成默认拷贝构造函数。事实上,默认拷贝构造函数可以应付许多情况。
示例
以下讨论中将用到的例子:
class CExample
{
public:
CExample(){pBuffer=NULL; nSize=0;}
~CExample(){delete pBuffer;}
void Init(int n){ pBuffer=new char[n]; nSize=n;}
private:
char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源
int nSize;
};
这个类的主要特点是包含指向其他资源的指针。
pBuffer指向堆中分配的一段内存空间。
编辑本段
一、拷贝函数
int main(int argc, char* argv[])
{
CExample theObjone;
theObjone.Init(40);
//现在需要另一个对象,需要将他初始化称对象一的状态
CExample theObjtwo=theObjone;
...
}
语句"CExample theObjtwo=theObjone;"用theObjone初始化theObjtwo。
其完成方式是内存拷贝,复制所有成员的值。
完成后,theObjtwo.pBuffer==theObjone.pBuffer。
即它们将指向同样的地方,指针虽然复制了,但所指向的空间并没有复制,而是由两个对象共用了。这样不符合要求,对象之间不独立了,并为空间的删除带来隐患。所以需要采用必要的手段来避免此类情况。
回顾一下此语句的具体过程:首先建立对象theObjtwo,并调用其构造函数,然后成员被拷贝。
可以在构造函数中添加操作来解决指针成员的问题。
所以C++语法中除了提供缺省形式的构造函数外,还规范了另一种特殊的构造函数:拷贝构造函数,上面的语句中,如果类中定义了拷贝构造函数,这对象建立时,调用的将是拷贝构造函数,在拷贝构造函数中,可以根据传入的变量,复制指针所指向的资源。
拷贝构造函数的格式为:构造函数名(对象的引用)
提供了拷贝构造函数后的CExample类定义为:
class CExample
{
public:
CExample(){pBuffer=NULL; nSize=0;}
~CExample(){delete pBuffer;}
CExample(const CExample); //拷贝构造函数
void Init(int n){ pBuffer=new char[n]; nSize=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)
{
if (this == RightSides) // 如果自己给自己赋值则直接返回
{
return *this;
}
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; //调用重载后的"="
}
编辑本段
四、拷贝构造与构造区别
class 类名
{
public:
类名(形参参数)//构造函数
类名(类名对象名)//拷贝构造函数
,,,,,,,,,,,,,,,,,,,,,
};
拷贝构造函数的实现:
类名::类名(类名对象名)//拷贝构造函数的实现
{函数体}
不完整的例子
拷贝构造函数:
Class Point
{
Public:
Point(int xx=0,int yy=m)(X=xx;Y=yy;)
Point(Point p);
Int getX() {return X;}
Int getY(){ return Y;}
Private :
Int X,Y;
}
Point::Point(Point p)
{
X=p.X;
Y=p.Y;
Cout"拷贝构造函数调用"endl;
}
位拷贝也叫浅拷贝。。。是编译器帮你自动生成的拷贝构造函数做的事情,它只能做一些基本的赋值操作,如果自己定义拷贝构造函数就要防止位拷贝。。因为位拷贝容易发生一些错误。。