C++定义一个类,如果什么方法都不写,默认会自动生成4个方法:构造函数、拷贝构造函数、赋值语句、析构函数。
大多数人只知道这四个,实际上还有2个,对一般对象的取地址,对常对象的取地址
1 #include<iostream> 2 using namespace std; 3 class Test { 4 public: 5 Test(int d = 0) :data(d) 6 { 7 cout << "Create Test Object:" << this << endl; 8 } 9 Test(const Test &t) 10 { 11 cout << "Copy Create Test Object:" << this << endl; 12 this->data = t.data; 13 } 14 Test& operator=(const Test &t) 15 { 16 cout << "Assign:" << this << "=" << &t << endl; 17 if (this != &t) 18 { 19 this->data = t.data; 20 } 21 return *this; 22 } 23 Test* operator&() 24 { 25 return this; 26 } 27 const Test* operator&()const 28 { 29 return this; 30 } 31 ~Test() 32 { 33 cout << "Free Test Object:" << this << endl; 34 } 35 private: 36 int data; 37 }; 38 39 int main(int argc, char **argv) 40 { 41 Test t; 42 Test t1 = t; 43 Test t2; 44 t2 = t1; 45 46 Test t3; 47 Test *pt = &t3; 48 49 const Test t4; 50 const Test *pt1 = &t4; 51 return 0; 52 }
如果构造函数内部开辟了内存空间,则需要在析构函数中释放内存。当使用对象初始化对象时,C++默认是浅拷贝,即只是指针只想相同内存。当其中的一个对象被析构,活着的对象还只想被释放的内存,然后当他死亡时再次释放内存,导致内存被释放多次,程序崩溃。
这段代码,变异不报错,但是运行时崩溃
1 #include<iostream> 2 using namespace std; 3 4 class String 5 { 6 public: 7 String(const char *str = "") 8 { 9 if (*str == '\0') 10 { 11 data = (char *)malloc(sizeof(char)); 12 data[0] = '\0'; 13 } 14 else 15 { 16 data = (char *)malloc(sizeof(char)*(strlen(str) + 1)); 17 strcpy(data, str); 18 } 19 } 20 ~String() 21 { 22 free(data); 23 data = NULL; 24 } 25 private: 26 char *data; 27 }; 28 29 int main(int argc, char **argv) 30 { 31 const char *str = "Hello"; 32 String s1(str); 33 String s2(s1); 34 }
改进代码,自己指定拷贝构造函数(深拷贝)
1 #include<iostream> 2 using namespace std; 3 4 class String 5 { 6 public: 7 String(const char *str = "") 8 { 9 if (*str == '\0') 10 { 11 data = (char *)malloc(sizeof(char)); 12 data[0] = '\0'; 13 } 14 else 15 { 16 data = (char *)malloc(sizeof(char)*(strlen(str) + 1)); 17 strcpy(data, str); 18 } 19 } 20 String(const String &s) 21 { 22 data= (char *)malloc(sizeof(char)*(strlen(s.data) + 1)); 23 strcpy(data, s.data); 24 } 25 ~String() 26 { 27 free(data); 28 data = NULL; 29 } 30 private: 31 char *data; 32 }; 33 34 int main(int argc, char **argv) 35 { 36 const char *str = "Hello"; 37 String s1(str); 38 String s2(s1); 39 }
同样,对于赋值函数也需要深拷贝。浅拷贝方式导致程序崩溃原因同上。增加赋值函数后代码
1 #include<iostream> 2 using namespace std; 3 4 class String 5 { 6 public: 7 String(const char *str = "") 8 { 9 if (*str == '\0') 10 { 11 data = (char *)malloc(sizeof(char)); 12 data[0] = '\0'; 13 } 14 else 15 { 16 data = (char *)malloc(sizeof(char)*(strlen(str) + 1)); 17 strcpy(data, str); 18 } 19 } 20 String(const String &s) 21 { 22 data= (char *)malloc(sizeof(char)*(strlen(s.data) + 1)); 23 strcpy(data, s.data); 24 } 25 String& operator=(const String &s) 26 { 27 if (this != &s) 28 { 29 data = (char *)malloc(sizeof(char)*(strlen(s.data) + 1)); 30 strcpy(data, s.data); 31 } 32 return *this; 33 } 34 ~String() 35 { 36 free(data); 37 data = NULL; 38 } 39 private: 40 char *data; 41 }; 42 43 int main(int argc, char **argv) 44 { 45 const char *str = "Hello"; 46 String s1(str); 47 String s2(s1); 48 String s3; 49 s3 = s1; 50 }
但是这段代码有内存泄露风险。如果String s3("World"); s3 = s1;就会内存泄漏。“World”字符串对应的内存泄漏,改进代码
1 #include<iostream> 2 using namespace std; 3 4 class String 5 { 6 public: 7 String(const char *str = "") 8 { 9 if (*str == '\0') 10 { 11 data = (char *)malloc(sizeof(char)); 12 data[0] = '\0'; 13 } 14 else 15 { 16 data = (char *)malloc(sizeof(char)*(strlen(str) + 1)); 17 strcpy(data, str); 18 } 19 } 20 String(const String &s) 21 { 22 data= (char *)malloc(sizeof(char)*(strlen(s.data) + 1)); 23 strcpy(data, s.data); 24 } 25 String& operator=(const String &s) 26 { 27 if (this != &s) 28 { 29 free(data); 30 data = NULL; 31 data = (char *)malloc(sizeof(char)*(strlen(s.data) + 1)); 32 strcpy(data, s.data); 33 } 34 return *this; 35 } 36 ~String() 37 { 38 free(data); 39 data = NULL; 40 } 41 private: 42 char *data; 43 }; 44 45 int main(int argc, char **argv) 46 { 47 const char *str = "Hello"; 48 String s1(str); 49 String s2(s1); 50 String s3("World"); 51 s3 = s1; 52 }
一般赋值语句的流程是:
1、是不是给自己赋值
2、释放原有空间
3、申请空间进行拷贝赋值
4、返回自身对象
如果一个类里面,他的数据成员包含指针,我们需要重新编写拷贝构造、赋值语句。这两个函数默认的会提供指针的浅拷贝,浅赋值