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