很早之前,一直对这些名词很不解,翻看了c++ primer很多次,也只知道其用法,不知其解。说白了,就是不知道为什么有这些各式的拷贝的存在,或者是说在何种环境下面要使用得上!今天无意翻到当初写的小例子,重新整理后帖上来作个记号!
//=================================
一,在什么环境下面可能需要使用自定义拷贝、赋值构造?
在解释这些名词之前,需要知道一点,在什么环境下面需要使用自定义拷贝、赋值构造,答案就是:当一个自定义的struct或class中包括有指针对象时(可能是堆栈资源或是文件等等,以前总以为只是普通指针),拷贝构造和赋值构造就可能需要自定义(是否重定义取决于类的实现模型是否需要,以前总以为是必须要实现),因为你需要明确的定义当class发生拷贝或赋值构造时,class中的那些指针对象所指向的资源也是否需要COPY一个副本(重新分配堆栈)。当没有自定义构造函数时,系统的默认拷贝和赋值构造函数只对指针进行位拷贝,即只对二个class中的指针进行”=”赋值,指针所指向的的堆栈资源仍然是一样的,这时就会发生二种情况:一, 其中一个类中指针对象被析构时,虽一个类的指针便会成野指针,调用时程序异常或崩溃;二,当二个类都析构时,二个指针对象所指向的堆栈会被delete二次,造成内存泄露。可是,可是也不是所有的指针对象在发生拷贝构造时都需要重新分配堆栈,请参第五条!
//=================================
二,名词解释:
拷贝构造、赋值构造:
拷贝构造:复制构造函数是一种特殊构造函数,具有单个形参,该形参(常用 const 修饰)是对该类类型的引用。当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数。当将该类型的对象传递给函数或函数返回该类型的对象时,将隐式使用复制构造函数。(c++ primer原话)
赋值构造:与复制构造的行为一样,只是通过重载“=”,通过=来将目标初始化自己。虽然行为一样,但实现时有区别,需要排除是否是 “自己=自己”, 是否有“先前的指针对象没有delete”的情况!
浅拷贝(位拷贝)、深拷贝(值拷贝):
浅拷贝:当发生拷贝或赋值构造时,系统将依次调用目标类的每一个成员变量的操作符“=”来初始化自己类的成员变量; 但二个class中都有指针对象时,二个指针进行”=”操作时,指针的值将是一样的(指向的堆栈是一样的)。
反之深拷贝:行为跟浅拷贝一样,只是针对指针对象时,被构造的类会先将为自己的针对对象分配新的堆栈,然后再将目标类中的指针所指向的堆栈进行memcpy。
三,=================================
demo:
class CFoo
{
public:
CFoo()
:m_pStr( NULL )
{
}
CFoo( const CFoo& rhs )
{
m_pStr = new std::string;
*m_pStr = *rhs.m_pStr;
}
void init( const std::string &str )
{
if ( !m_pStr )
{
m_pStr = new std::string;
*m_pStr = str;
}
}
CFoo& operator=( const CFoo& rhs )
{
if ( this == &rhs ) // 判断是否是自己给自己赋值
return *this;
if ( m_pStr ) // 判断是不是已经申请过堆栈的指针,没有这一步,会导致已申请过的堆栈没有进行delete导致的内存泄露
{
delete m_pStr;
m_pStr = NULL;
}
m_pStr = new std::string;
*m_pStr = *rhs.m_pStr;
return *this;
}
~CFoo()
{
if ( m_pStr )
{
delete m_pStr;
m_pStr = NULL;
}
}
void printSth()
{
if ( m_pStr )
{
std::cout << " this: " << this << " , m_pStr addrs: " << m_pStr << " , m_pStr value: " << *m_pStr << std::endl;
}
else
{
std::cout << " this: " << this << " , m_pStr is NULL " << std::endl;
}
}
private:
std::string *m_pStr;
};
int _tmain(int argc, _TCHAR* argv[])
{
/////////////////////////////////////////////////////
// [ 拷贝构造和赋值 ]
CFoo foo;
foo.init( "test!!!");
foo.printSth();
CFoo foo2( foo ); // 拷贝构造
foo2.printSth();
CFoo foo3 = foo; // 这里并不是赋值,而是拷贝构造
foo3.printSth();
CFoo foo4;
foo4.printSth();
foo4 = foo; // 这里才是赋值构造
foo4.printSth();
//////////////////////////////////////////////////////
return 0;
}
//=================================
四,何时会发生拷贝构造?
以下情况都会调用拷贝构造函数:
(1)一个对象以值传递的方式传入函数体
(2)一个对象以值传递的方式从函数返回
(3)一个对象需要通过另外一个对象进行初始化。
//=================================
五,在什么环境下面可能需要使用浅拷贝、深拷贝?
无论深浅,都是需要的。当深拷贝发生时,通常表明存在着一个“聚合或组合的关系“,而浅拷贝发生时,通常表明存在着一个弱的“关联关系”。
二种情况:
1,指针成员对象是class的成员变量,指针的堆栈和生命周期都是由class自己来维护和管理,那么,当拷贝发生时,需要自定义拷贝、赋值构造函数,对指针指向的资源进行深拷贝。
2,指针成员对象是class的成员变量,但堆栈的申请和生命周期由其它类来维护和管理,那么,这个类发生拷贝时,只能进行浅拷贝。