博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【C/C++学院】(6)构造函数/析构函数/拷贝构造函数/深copy浅copy
阅读量:6699 次
发布时间:2019-06-25

本文共 7048 字,大约阅读时间需要 23 分钟。

1.构造函数

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

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

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

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

#include 
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
#include
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 
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 
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 
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 
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<
<

#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;}

最终分析图:

你可能感兴趣的文章
第 三 十 八 天:Linux 的 LVM 逻 辑 卷 管 理
查看>>
Flex通过Blazeds利用Remoteservice与后台java消息推送
查看>>
python3 实现对比conf 文件差异
查看>>
vueX的使用
查看>>
Android的TextView在显示文字的时候,如果有段中文有英文,有中文,有中文标点符号,你会发现,当要换行的时候遇到中文标点, 这一行就会空出很多空格出来...
查看>>
bupt summer training for 16 #3 ——构造
查看>>
github 如何设置项目的语言显示
查看>>
树莓派(Raspberry Pi):完美的家用服务器
查看>>
微信jssdk遇到的一些问题汇总
查看>>
Code Chef December Challenge 2018题解
查看>>
【IntelliJ IDEA】添加一个新的tomcat,tomcat启动无法访问欢迎页面,空白页,404
查看>>
PE文件RV转FOA及FOA转RVA
查看>>
哪些要素会让咱们呈现抑郁症的病症
查看>>
mysql
查看>>
使用vue+webpack从零搭建项目
查看>>
linux命令二
查看>>
《面向模式的软件体系结构2-用于并发和网络化对象模式》读书笔记(13)--- 线程安全接口和双检查加锁优化...
查看>>
navicat 官方使用手册,中文版,快捷键大全
查看>>
结对开发:电梯调度(2)
查看>>
Java中的继承性特性
查看>>