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;
}
最终分析图: