C++对象的构造(下)

    在C++中,如果我们定义一个类,没有定义任何的构造函数,那么编译器会为我们提供两个特殊的构造函数:无参构造函数拷贝构造函数

    无参构造函数: 其实就是一个没有参数、函数体为空的构造函数
    拷贝构造函数: 函数参数为 const class_name&,拷贝构造函数只是简单的进行成员变量的赋值

下边来看下边两个类test1和test2
test1中我们没有定义任何构造函数,实则编译器会默认提供一个无参构造函数和一个拷贝构造函数,这时候test1类中其实是有一个无参构造函数test1(){}和一个拷贝构造函数test1(const test1& obj){}

class test1
{

};

class test2
{
public:
    test2(){}
    test2(const test2& obj){}
};

对于无参构造函数,比较简单,下边我们重点来看看拷贝构造函数,上边说了,编译器提供的默认拷贝构造函数,能简单的进行对象成员的赋值,那么我们以下边的代码来验证验证:

#include <iostream>
#include <string>

using namespace std;

class test1
{
private:
    int m_i;
    int m_j;
public:
    void setI(int i)
    {
    	m_i = i;
    }

    void setJ(int j)
    {
    	m_j = j;
    }

    int getI()
    {
    	return m_i;
    }
    int getJ()
    {
    	return m_j;
    }
};



int main()
{
    test1 t1;
    t1.setI(12);
    t1.setJ(34);

    test1 t2 = t1;  //这里会调用拷贝构造函数,由于我们没有定义任何构造函数,所以使用编译器提供的默认拷贝构造函数

    cout << "t1.getI() = " << t1.getI() << endl;
    cout << "t1.getJ() = " << t1.getJ() << endl;

    cout << "t2.getI() = " << t2.getI() << endl;
    cout << "t2.getJ() = " << t2.getJ() << endl;

    system("pause");
}

编译输出:

从代码中我们看到,对于test1类,我们没有定义任何构造函数,所以编译器会为我们提供两个默认的构造函数,一个无参构造函数,一个拷贝构造函数,在执行到test1 t2 = t1 这条语句时,会调用拷贝构造函数,将t1的内容复制一份给t2,跟我们C语言中的一个变量赋值给另一个变量道理是一样的,这里由于我们没有定义拷贝构造函数,所以调用的是编译器提供的默认拷贝构造函数,进行成员m_i与m_j的值得赋值,从而使得t2中的成员变量与t1中的成员变量一致。

    相信你对C++的拷贝构造函数已经有了一个初步的认识,但是在C++中,拷贝构造函数还分为深拷贝浅拷贝
    深拷贝:对象的逻辑状态相同
    浅拷贝:对象的物理状态相同(即只是对对象成员进行简单的复制)

编译器为我们提供的只是浅拷贝

    在讨论深拷贝前,我们先来看下边的一段代码:

#include <iostream>
#include <string>

using namespace std;

class test1
{
private:
    int m_i;
    int m_j;
    int *m_p;	//增加一个指针成员变量
public:

    test1()
    {
    	m_p = new int(100);
    }

    void setI(int i)
    {
    	m_i = i;
    }

    void setJ(int j)
    {
    	m_j = j;
    }

    int getI()
    {
    	return m_i;
    }
    int getJ()
    {
    	return m_j;
    }

    int* getP()
    {
    	return m_p;
    }

    void freeP()
    {
    	delete m_p;
    }
};



int main()
{
    test1 t1;
    t1.setI(12);
    t1.setJ(34);

    test1 t2 = t1;


    cout << "t1.getI() = " << t1.getI() << endl;
    cout << "t1.getJ() = " << t1.getJ() << endl;
    cout << "t1.getP() = " << t1.getP() << endl << endl;

    cout << "t2.getI() = " << t2.getI() << endl;
    cout << "t2.getJ() = " << t2.getJ() << endl;
    cout << "t2.getP() = " << t2.getP() << endl;

    t1.freeP();	//释放t1中的指针m_p
    t2.freeP(); //释放t2中的指针m_p

    system("pause");
}

编译运行一下:

可以看到,程序其实已经崩溃了,分析一下就知道是执行t1.freeP()和t2.freeP()这两个函数奔溃的,细心的朋友会发现,t1.getP()跟t2.getP()返回的值是一样的,也就是说t1和t2对象的成员指针变量m_p是一样,t1和t2执行freeP()函数时,导致同一个地址释放了两次,所以程序会奔溃,上边代码执行默认的拷贝构造函数后,其实对象的指针成员变量就如下图所示,指向的是同一片内存空间。


    为了解决程序奔溃问题,我们首先来分析下上边的代码,前边我们说过,我们如果没有定义构造函数,那么编译器会默认提供一个拷贝构造函数,而提供的拷贝构造函数只是浅拷贝,也就是只能简单的进行对象成员的复制,所以在执行test1 t2 = t1时,只是简单的把t1中的m_p变量的值赋值给 t2中m_p变量,并没有分配额外的空间,这就导致了同一个内存地址会释放两次的原因。

    有了以上的分析,我们就可以重写拷贝构造函数来解决这个问题,改写的代码如下,其中我们重新实现了拷贝构造函数:
 

#include <iostream>
#include <string>

using namespace std;

class test1
{
private:
    int m_i;
    int m_j;
    int *m_p;	//增加一个指针成员变量
public:

    test1()
    {
    	m_p = new int(100);
    }

    test1(const test1& obj)	//重新实现拷贝构造函数
    {
    	m_i = obj.m_i;
    	m_j = obj.m_j;

    	m_p = new int;	//新申请内存空间
    	*m_p = *(obj.m_p);	//将obj的m_p指向的内存空间的值赋值给m_p指向的新申请内存空间
    }

    void setI(int i)
    {
    	m_i = i;
    }

    void setJ(int j)
    {
    	m_j = j;
    }

    int getI()
    {
    	return m_i;
    }
    int getJ()
    {
    	return m_j;
    }

    int* getP()
    {
    	return m_p;
    }

    int getPValue()
    {
    	return *m_p;
    }

    void freeP()
    {
    	delete m_p;
    }
};



int main()
{
    test1 t1;
    t1.setI(12);
    t1.setJ(34);

    test1 t2 = t1;


    cout << "t1.getI() = " << t1.getI() << endl;
    cout << "t1.getJ() = " << t1.getJ() << endl;
    cout << "t1.getP() = " << t1.getP() << endl;
    cout << "t1.getPValue() = " << t1.getPValue() << endl << endl;

    cout << "t2.getI() = " << t2.getI() << endl;
    cout << "t2.getJ() = " << t2.getJ() << endl;
    cout << "t2.getP() = " << t2.getP() << endl;
    cout << "t2.getPValue() = " << t2.getPValue() << endl;

    t1.freeP();	//释放t1中的指针m_p
    t2.freeP(); //释放t2中的指针m_p

    system("pause");
}

 编译运行一下:

可以看到,t1和t2中的m_p指针变量的值已经不想同了,此时两个对象释放的就不是同一个内存空间了;to它们同时的空间的值是相同的,都为100,这就是我们想要的,这就是我们所说的深拷贝。

    C++中需要深拷贝的情况一般是有成员指代了系统资源,如动态使用内存空间、打开外部文件、使用网络端口等等,就需要深拷贝。

    在C++中默认的潜规则是,如果自己要实现拷贝构造函数,必须要实现成深拷贝

最后总结一下:

    -C++编译器会默认提供构造函数
    -无参构造函数用于定义对象的默认初始化状态
    -拷贝构造函数在创建对象时拷贝对象的状态
    -对象的拷贝有浅拷贝(物理状态相同)和深拷贝(逻辑状态相同)
    -自己定义的拷贝构造函数,必须要实现深拷贝
 

猜你喜欢

转载自blog.csdn.net/lms1008611/article/details/81291845
今日推荐