首页 > 代码库 > 【C/C++学院】(6)构造函数/析构函数/拷贝构造函数/深copy浅copy

【C/C++学院】(6)构造函数/析构函数/拷贝构造函数/深copy浅copy

1.构造函数

    类的初始化即为构造函数。也为:隐式的初始化。

构造函数在对象初始化的时候,自动被调用。隐式的调用。

构造函数分为三种:有参构造函数、无参构造函数、拷贝构造函数。

有参构造函数调用有三种:括号法、等号法、手工法。

#include <iostream>
using namespace std;

class Test
{
private:
	int m_a;
public:
	Test()//无参构造函数
	{	}
	Test(const Test &obj)//拷贝构造函数
	{	}
	Test(int a)//有参构造函数
	{
		m_a = a;
	}
	void print()
	{
		cout << "m_a:" << m_a << endl;
	}
	
};

void main()
{
	Test t1(10);//括号法  //c++编译器自动调用这个类的有参构造函数
	t1.print();
	Test t2 = 20;//等号法  //c++编译器自动调用这个类的有参构造函数
	t2.print();
	Test t3 = Test(30);//手工法 //程序员手工的调用构造函数 进行对象初始化
	t3.print();
	system("pause");
}

2.析构函数

析构函数(destructor) 与构造函数相反,当对象脱离其作用域时(例如对象所在的函数已调用完毕),系统自动执行析构函数。析构函数往往用来做“清理善后” 的工作(例如在建立对象时用new开辟了一片内存空间,应在退出前在析构函数中用delete释放)。
主函数结束的同时,对象stud1,stud2均应被“清理”,而清理就是通过调用了析构函数实现的。
#define _CRT_SECURE_NO_WARNINGS
#include<string>
#include<iostream>
using namespace std;

class stud//声明一个类
{
private://私有部分
	int num;
	char name[10];
	char sex;
public://公用部分
	stud(int n, char nam[], char s)//构造函数
	{
		num = n;
		strcpy(name, nam);
		sex = s;
	}
	~stud()//析构函数
	{
		cout << "stud has been destructed!" << endl;//通过输出提示告诉我们析构函数确实被调用了
	}
	void display()//成员函数
	{
		cout << "num:" << num << endl;
		cout << "name:" << name << endl;
		cout << "sex:" << sex << endl;
	}
};
int main()
{
	stud stud1(10010, "Wang-li", 'f');
	stud stud2(10011, "Zhang-fun", 'm');//建立两个对象
	stud1.display();//输出学生1的数据
	stud2.display();//输出学生2的数据
	system("pause");
	return 0;
}

3.拷贝构造函数

拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的形参必须是引用,但并不限制为const,一般普遍的会加上const限制。此函数经常用在函数调用时用户定义类型的值传递及返回。拷贝构造函数要调用基类的拷贝构造函数和成员函数。如果可以的话,它将用常量方式调用,另外,也可以用非常量方式调用。

当我们没有编写拷贝构造函数的时候,c++编译器会默认给我们提供一个拷贝构造函数,执行的是浅拷贝。

copy构造函数四种应用场景;

第一种场景:=

#include <iostream>
using namespace std;
class CExample {
private:
    int a;
public:
    //构造函数
    CExample(int b)
    {
        a = b;
    }
    //拷贝构造函数
    CExample(const CExample& C)
    {
        a = C.a;
    }
    //一般函数
    void Show()
    {
        cout << a << endl;
    }
};
int main()
{
    CExample A(100);    
    CExample B = A; //注意这里的对象初始化要调用拷贝构造函数,而非赋值
    // CExample B(A); 也是一样的
    B.Show();
    return 0;
}  

第二种场景:()

#include <iostream>
using namespace std;
class CExample {
private:
    int a;
public:
    //构造函数
    CExample(int b)
    {
        a = b;
    }
    //拷贝构造函数
    CExample(const CExample& C)
    {
        a = C.a;
    }
    //一般函数
    void Show()
    {
        cout << a << endl;
    }
};
int main()
{
    CExample A(100);    
    //CExample B = A; //注意这里的对象初始化要调用拷贝构造函数,而非赋值
    CExample B(A); //也是一样的
    B.Show();
    return 0;
}  

第三种场景:对象以值传递的方式传入函数参数

#include <iostream>
using namespace std;
class CExample
{
private:
    int a;
public:
    //构造函数  
    CExample(int b)
    {
        a = b;
        cout << "creat: " << a << endl;
    }
    //拷贝构造  
    CExample(const CExample& C)
    {
        a = C.a;
        cout << "copy" << endl;
    }
    //析构函数  
    ~CExample()
    {
        cout << "delete: " << a << endl;
    }
    void Show()
    {
        cout << a << endl;
    }
};
//全局函数,传入的是对象  
void g_Fun(CExample C)
{
    cout << "test" << endl;
}
int main()
{
    CExample test(1);
    //传入对象  
    g_Fun(test);
    return 0;
} 

第四种场景:对象以值传递的方式从函数返回

#include <iostream>
using namespace std;
class CExample
{
private:
    int a;
public:
    //构造函数  
    CExample(int b=0)
    {
        a = b;
        cout << "a:" << a << endl;
    }
    ~CExample()
    {
        cout << "destroy a:" << a << endl;
    }
    //拷贝构造  
    CExample(const CExample& C)
    {
        a = C.a;
        cout << "copy a:"<< a << endl;
    }
};
//全局函数  
CExample g_Fun()
{
    CExample temp(10);
    return temp;
}
int main()
{
    CExample ret;
    ret = g_Fun();
    return 0;
}  
添加断点,逐条语句运行,观察程序的执行步骤以及打印输出:

技术分享

4.深copy浅copy

    在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

  深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。

#include "iostream"

using namespace std;



class name

{ 

public :

	name(char *pn) ;  

	name( name &obj)

	{

		cout <<" copy Constructing " << endl ;

		char *pn = obj.getPn();

		pname = (char *)malloc(strlen(pn) +1);

		if (pname!=NULL) strcpy(pname,pn) ;

		//pname = new char[strlen(pn)+1] ;

		//if (pname!=0) strcpy(pname,pn) ;

		size = strlen(pn) ;

	}

	~ name() ;

protected : 

	char *pname ;       int size ;

public:

	char * getPn()

	{

		return pname;

	}	

	void operator=(name &obj1)

	{

		cout <<" 执行=操作" << endl ;

		char *pn = obj1.getPn();

		pname = (char *)malloc(strlen(pn) +1);//此处malloc了内存,没有free,存在一个潜在的bug

		if (pname!=NULL) strcpy(pname,pn) ;

		//pname = new char[strlen(pn)+1] ;

		//if (pname!=0) strcpy(pname,pn) ;

		pname[0] = 'm';

		size = strlen(pn) ;

	}

} ;

name::name(char *pn)

{ 

	cout <<" Constructing " << pn << endl ;

	pname = (char *)malloc(strlen(pn) +1);

	if (pname!=0) strcpy(pname,pn) ;

	//pname = new char[strlen(pn)+1] ;

	//if (pname!=0) strcpy(pname,pn) ;

	size = strlen(pn) ;

} 

name :: ~ name()

{ 

	cout << " Destructing " << pname << endl ; 

	pname[0] = '\0' ;

	//delete  []pname ;

	free(pname);

	size = 0 ;

}
void playmain()
{
	name obj1("name1");
	//如果你不写copy构造函数,那么C++编译器会给我们提供一个默认的copy构造函数 (浅cpy)
	name obj2 = obj1;
	//如果你不写=操作,那么C++编译器会给我们提供一个=操作函数 (浅cpy)
	obj2 = obj1;
	cout<<obj2.getPn()<<endl;
}
void main()
{
	playmain();
	system("pause");
}
技术分享

#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std;
class name
{
public:
    name(char *pn);
    name(name &obj)
    {
        cout << " copy Constructing " << endl;
        char *pn = obj.getPn();
        pname = (char *)malloc(strlen(pn) + 1);
        if (pname != NULL) strcpy(pname, pn);
        //pname = new char[strlen(pn)+1] ;
        //if (pname!=0) strcpy(pname,pn) ;
        size = strlen(pn);
    }
    ~name();
protected:
    char *pname;       int size;
public:
    char * getPn()
    {
        return pname;
    }
    void operator=(name &obj1)
    {
        cout << " 执行=操作" << endl;
        if (pname != NULL)//此处有一个疑问:如果不free, 直接将原来的内存进行重新赋值
        {
            char *pn = obj1.getPn();
            strcpy(pname, pn);
            pname[0] = 'N';
            size = strlen(pn);
        }
    }
    /*
    void operator=(name &obj1)
    {
        cout <<" 执行=操作" << endl ;
        if (pname != NULL)//此处有一个疑问:如果不free, 直接将原来的内存进行重新赋值
        {
            free(pname);
            pname = NULL;
            size = 0;
        }
        char *pn = obj1.getPn();
        pname = (char *)malloc(strlen(pn) +1);
        if (pname!=NULL) strcpy(pname,pn) ;
        //pname = new char[strlen(pn)+1] ;
        //if (pname!=0) strcpy(pname,pn) ;
        pname[0] = 'm';
        size = strlen(pn) ;
    }
    */
};
name::name(char *pn)
{
    cout << " Constructing " << pn << endl;
    pname = (char *)malloc(strlen(pn) + 1);
    if (pname != 0) strcpy(pname, pn);
    //pname = new char[strlen(pn)+1] ;
    //if (pname!=0) strcpy(pname,pn) ;
    size = strlen(pn);
}
name :: ~name()
{
    cout << " Destructing " << pname << endl;
    pname[0] = '\0';
    //delete  []pname ;
    free(pname);
    size = 0;
}
int playmain()
{
    name obj1("name1");
    name obj3("name3");
    //如果你不写copy构造函数,那么C++编译器会给我们提供一个默认的copy构造函数 (浅cpy)
    name obj2 = obj1;
    //做业务逻辑
    //此处省略500行
    //如果你不写=操作,那么C++编译器会给我们提供一个=操作函数 (浅cpy)
    //会调用对象2 的=号操作函数, obj3是形参, obj2干什么去了?
    obj2 = obj3;
    cout << obj2.getPn() << endl;
    return 0;
}
int main()
{
    playmain();
    //system("pause");
    return 0;
}
技术分享

最终分析图:

技术分享


【C/C++学院】(6)构造函数/析构函数/拷贝构造函数/深copy浅copy