本帖最后由 baizy77 于 2018-10-2 11:10 编辑
版权声明 --------------------------------------------------------------------------------------------------------------------- 作者: 女儿叫老白 (白振勇) 转载请注明出处! --------------------------------------------------------------------------------------------------------------------- -----------------------------------------------------------------------------
引言: ---------------------------------------------------------------------------- 之前我们多次提到过拷贝构造函数,但是我们仍然存在一些疑问: 1, 拷贝构造函数有什么作用? 2, 按位拷贝是什么意思? 3, 什么情况下,编译器才总是调用我们的拷贝构造函数,而不是执行位拷贝? 4, 什么情况下,编译器会创建默认的拷贝构造函数?反之,什么情况下,编译器不会自动创建默认的拷贝构造函数? 5, 如何防止按值传递对象? 今天我们来找一下这些问题的答案。 正文: ---------------------------------------------------------------------------- 一般情况下,如果我们没有编写类的拷贝构造函数,编译器就会执行按位拷贝: 代码清单: ---------------------------------------------------------------------------- // myclass.h class CMyClass { public: CMyClass():m_nValue(0) {} ~CMyClass(){} voidsetValue(int n) {m_nValue = n;} intgetValue() {return m_nValue;} private: intm_nValue;
};
// main.cpp int func(CMyClass mc) { int ret = mc.getValue(); returnret; }
int main(int argc char*argv[]) { CMyClass myclass; int ret = func(myclass); } ---------------------------------------------------------------------------- 在上述代码中,int ret = func(myclass)这一处代码,对于int func(CMyClass mc)函数来说,因为参数mc并没有使用引用,因此编译器在调用func()时,只是对myclass对象执行按位拷贝,也就是把myclass变量的内部成员数据原封不动的拷贝一份给新的临时变量,然后作为参数传递给func()。 大家可能觉得奇怪,把旧对象的内部变量拷贝一份给新对象有什么问题吗?要回答这个问题,我们来看一种情况: 代码清单: ---------------------------------------------------------------------------- // myclass2.h class CMyClass2 { public: CMyClass2():m_nValue (0), m_pBuffer(NULL) {} ~CMyClass2(); voidsetCount(int n); intgetCount () {return m_nCount;} private: intm_nCount; char*m_pBuffer;
};
// myclass2.cpp void CMyClass2 ::setCount(int n) { if (n < 0) { return; } m_nCount= n; if(NULL != m_pBuffer) { delete[] m_pBuffer; } if(m_nCount> 0 ) { m_pBuffer = new char(m_nCount); } } CMyClass2 ::~CMyClass2(){ if (NULL != m_pBuffer) { delete[] m_pBuffer; } }
// main.cpp int func(CMyClass2 mc) { int ret = mc.getCount (); returnret; }
int main(int argc char*argv[]) { CMyClass2 myclass; int ret = func(myclass); }
---------------------------------------------------------------------------- 从上述代码可以看出,CMyClass2这个类拥有一个指针m_pBuffer,如果在ain()函数中调用fun()时编译器仍然按照按位拷贝,那么myclass与传入func()时编译器产生的临时对象的m_pBuffer成员将会指向同一块内存,而func()函数调用完成后,编译器产生的临时对象就会析构,看一下CMyClass2的析构函数就能知道,m_pBuffer所指向的内存将被释放,而这是我们不希望看到的。 所以,由编译器负责执行按位拷贝传递参数的方法,在有些时候并不靠谱。那么课改怎么解决这个问题呢?方法就是提供类的拷贝构造函数。 代码清单: ---------------------------------------------------------------------------- // myclass3.h class CMyClass3 { public: CMyClass3():m_nValue (0), m_pBuffer(NULL) {} CMyClass3(constCMyClass3& right); ~CMyClass3(); voidsetCount (int n); intgetCount () const {return m_nCount;}
constchar* getBuffer() const {return m_pBuffer}; private: intm_nCount char*m_pBuffer;
};
// myclass3.cpp void CMyClass3::setCount(int n) { if (n < 0) { return; } m_nCount= n; if(NULL != m_pBuffer) { delete[] m_pBuffer; } if(m_nCount> 0 ) { m_pBuffer = new char(m_nCount); } } CMyClass ::~CMyClass(){ if (NULL != m_pBuffer) { delete[] m_pBuffer; } } CMyClass3:: CMyClass3(constCMyClass3& right) { setCount(right.getCount()); memcpy(m_pBuffer, right.getBuffer(), right.getCount()); } ---------------------------------------------------------------------------- 在CMyClass3中,我们提供了拷贝构造函数: CMyClass3(const CMyClass3&); 它提供一个const引用的参数,参数类型与类本身一致。在拷贝构造函数内部,我们编写了一些特殊代码,除了将成员变量做相应的赋值之外,还做了开辟内存、复制内存的操作,而编译器提供的按位拷贝是无法完成这些操作的。 大家可能注意到了,我们把int getCount()函数结尾增加了const限定符,表明该接口内部不会改变类的成员变量,这是因为拷贝构造函数内部要调用这个接口,而拷贝构造函数的入口参数传的是const引用,所以,getCount()接口需要定义为const。否则编译器会报错。 那么,什么情况下编译器才总是调用我们的拷贝构造函数,而不是执行位拷贝呢?那就是当我们提供了类的拷贝构造函数的情况下,编译器就不再执行位拷贝,而是调用我们的拷贝构造函数了。 那什么情况下,编译器会创建默认的拷贝构造函数?答案是按值传递,且我们没有写拷贝构造函数。所谓按值传递,指的是没有按照引用或者变量地址(指针)的方式传递参数。 反之,什么情况下,编译器不会自动创建默认的拷贝构造函数?答案是:当我们声明了私有拷贝构造函数。也就是我们声明了拷贝构造函数,并且把它声明为private。 那么如何防止按值传递对象?答案是:我们声明私有的拷贝构造函数,甚至不用定义它。也就是仅声明,不用实现拷贝构造函数。
结语: ---------------------------------------------------------------------------- 拷贝构造函数在我们进行软件开发的过程中非常重要,我们需要充分的利用上面讲到的一些原则或者特性来操作,根据我们不同的开发场景提供不同的编码实现。
参考资料 ---------------------------------------------------------------------------- 《C++编程思想》两卷合订本中文版(11章),(美) Bruce Eckel Chuck Allison著
|