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

时间:2021-08-18 19:52:52

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;
}
添加断点,逐条语句运行,观察程序的执行步骤以及打印输出:

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

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");
}
【C/C++学院】(6)构造函数/析构函数/拷贝构造函数/深copy浅copy

#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

最终分析图:

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