zoukankan      html  css  js  c++  java
  • 对象的构造和析构函数

      构造函数和析构函数,分别对应变量的初始化和清理,变量没有初始化,使用后果未知;没有清理,则会内存管理出现安全问题。

    构造函数和析构函数写法

      构造函数:与类名相同,没有返回值,不写void,可以发生重载,可以有参数,编译器自动调用,只调用一次。

      析构函数:~类名,没有返回值,不写void,不可以发生重载,不可以有参数,编译器自动调用,只调用一次。

    构造函数和析构函数的作用域是public下才可以调用到,不写默认private,调用不到。

      如果程序猿不提供,系统会默认提供,构造和析构函数,函数体为空。

     1 class Person{
     2 public:
     3     Person(){
     4         cout << "无参构造函数" << endl;
     5     }
     6     ~Person(){
     7         cout << "无参析构函数" << endl;
     8     }
     9 };
    10 
    11 void test01(){
    12     Person p;
    13 }
    14 
    15 int main(){
    16     
    17     test01();
    18     system("pause");
    19     return 0;
    20 }

    构造函数的分类和调用

      按照参数分类:有参构造和无参构造;按照类型分类:普通构造和拷贝构造函数。

     1 class Person{
     2 public:
     3     Person(const Person &a){
     4         age = a.age;
     5         cout << "拷贝构造函数" << endl;
     6     }
     7     ~Person(){
     8         cout << "无参析构函数" << endl;
     9     }
    10 
    11     int age;
    12 };

      拷贝构造函数必须加const,因为防止修改,本来就是用现有的对象初始化新的对象。

     1 class Person{
     2 public:
     3     Person(){
     4         cout << "无参构造函数" << endl;
     5     }
     6     Person(int a){
     7         cout << "有参构造函数" << endl;
     8     }
     9     Person(const Person &a){
    10         cout << "拷贝构造函数" << endl;
    11     }
    12     ~Person(){
    13         cout << "无参析构函数" << endl;
    14     }
    15 };
    16 
    17 void test02(){
    18 
    19     Person p(1); //有参构造函数
    20     Person p1(p); //拷贝构造函数
    21     Person p2; //无参构造函数  不能写成Person p2(); 编译器以为是声明,不是构造函数
    22 
    23     //显示调用
    24     Person p4 = Person(100); //有参构造函数
    25     Person p5 = Person(p4); //拷贝构造函数
    26     //Person(100);单独这句叫匿名对象,编译器如果发现对象是匿名的,在这行代码之后就释放对象
    27     //Person(p5);不能用拷贝构造函数初始化匿名对象,写成左值,编译器认为是Person p5,对象的声明,写成右值表示赋值构造函数
    28 
    29     //隐式调用
    30     Person p7 = 100; //隐式类型转换,相当于Person p7 = Person(100);编译器找到一个有参构造进行转换
    31     Person p8 = p7; //相当于Person p8 = Person(p7);
    32 }

      注意事项:(1)无参构造函数使用:Person p2; 不能写成Person p2(); 编译器以为是声明,不是构造函数;(2)显示调用和隐式调用的区别;(3)匿名对象的特点

     拷贝构造函数使用的时机

      (1)使用已经创建好的对象初始化新对象;(2)以值传递的方式来给函数参数传值;(3)以值方式返回局部对象(不常用,一般不返回局部对象)

     1 //使用已经创建好的对象初始化新对象
     2 void test03(){
     3     Person p1;
     4     p1.age = 10;
     5     Person p2(p1); 
     6 }
     7 
     8 //以值传递的方式来给函数参数传值
     9 void dowork(Person p1){ //值传递方式,实际上形参是一个拷贝,因此Person p1 = Person(p);
    10 
    11 }
    12 
    13 void test04(){
    14     Person p;
    15     p.age = 10;
    16     dowork(p);
    17 }
    18 
    19 //以值方式返回局部对象
    20 Person dowork2(){
    21     Person p1;
    22     return p1;
    23 }
    24 
    25 void test05(){
    26     Person p = dowork2();
    27 }

      对于使用已创建好的对象进行初始化对象时的补充:

     1 void test01(){
     2     Person p1; //无参构造函数
     3     Person *p2 = new Person(p1); //拷贝构造函数
     4 
     5     Person *p3 = new Person(30); //有参构造函数
     6     Person *p4 = new Person(*p3); //拷贝构造函数
     7 
     8     Person p5 = p1; //拷贝构造函数
     9     Person *p6 = p3; //赋值指针,和p3操作指针地址相同
    10 }

      注意事项:第三种返回局部对象,一般Debug下是先21行使用无参构造,再22行返回对象的拷贝,然后Person p = p1;但Release下是先26行处理Person p;dowork2(Person &p);然后再21行,只有无参构造。

    构造函数的调用规则

      系统会默认给一个类提供三个函数:默认构造函数(无参,函数体为空)、默认拷贝构造和析构函数(无参,函数体为空),其中默认拷贝构造可以实现简单的值拷贝。

      提供了有参构造函数,就不提供默认构造函数;提供了拷贝构造函数,就不会提供其他构造函数。

    深拷贝和浅拷贝

      只有当对象的成员属性在堆区开辟空间内存时,才会涉及深浅拷贝,如果仅仅是在栈区开辟内存,则默认的拷贝构造函数和析构函数就可以满足要求。

     1 class Person{
     2 public:
     3     Person(){
     4     }
     5 
     6     //有参构造
     7     Person(char *name_, int age_){
     8         name = (char *)malloc(strlen(name_)+1);//可以直接调用现有的成员属性
     9         strcpy(name, name_);
    10 
    11         age = age_;
    12     }
    13 
    14     //自定义拷贝构造函数
    15     Person(const Person &a){
    16         age = a.age;
    17         name = (char *)malloc(strlen(a.name) + 1);
    18         strcpy(name, a.name);
    19     }
    20 
    21     ~Person(){
    22         if (name != NULL){
    23             free(name);
    24             name = NULL;
    25         }
    26     }
    27 
    28     char *name;
    29     int age;
    30 };
    31 
    32 void test05(){
    33     //这样的初始化是栈区初始化,默认拷贝构造函数和默认析构函数也是可以的
    34     //Person p;
    35     //p.name = "namnana";
    36     //p.age = 23;
    37 
    38     //调用有参构造这是堆区初始化,必须自定义拷贝构造函数和析构函数
    39     Person p("namnana", 23); 
    40 
    41     Person p1(p);
    42 }

       具体原因则是默认构造函数只能实现值拷贝,因此涉及堆区开辟内存时,会将两个成员属性指向相同的内存空间,从而在释放时导致内存空间被多次释放,使得程序down掉。C语言中结构体的深拷贝和浅拷贝相同

     初始化列表

      一般通过有参构造进行初始化,另外也可以通过初始化列表进行初始化

     1 class Person{
     2 public:
     3     Person(){
     4     }
     5 
     6     //有参构造初始化数据
     7     Person(int a_, int b_, int c_){
     8         a = a_;
     9         b = b_;
    10         c = c_;
    11     }
    12 
    13     //1. 初始化列表进行初始化数据
    14     Person(int a_, int b_, int c_) : a(a_), b(b_), c(c_){}
    15 
    16     //2. 这种情况是固定参数初始化
    17     Person() :a(10), b(20), c(30){}
    18 
    19     int a;
    20     int b;
    21     int c;
    22 };
    23 
    24 void test05(){
    25     Person p1(10, 20, 30);//针对1.
    26     Person p2;//针对2.
    27 }

      类对象作为类成员属性,构造顺序先将对象一一构造,然后构造自己,析构的顺序是相反的。 

    explicit关键字

      为了防止构造函数中的隐式类型转换,加了explicit,只能显式调用

     1 class Person{
     2 public:
     3     Person(){
     4     }
     5 
     6     //有参构造初始化数据
     7     explicit Person(const char*str_){
     8         str = (char *)malloc(sizeof(char)*100);
     9         strcpy(str,str_);
    10     }
    11 
    12     ~Person() {
    13         if (str != NULL){
    14             free(str);
    15             str = NULL;
    16         }
    17     }
    18 
    19     char *str;
    20 };
    21 
    22 void test05(){
    23     //Person p = "abc"; 隐式调用
    24 
    25     Person p ("abc"); //显式调用
    26 }
  • 相关阅读:
    FIR滤波器相关解释
    FIR数字信号滤波器
    图像中的插值
    对DDS的深度认识
    嵌入式媒体处理(EMP)中的编码和解码
    FPGA噪声干扰
    视频压缩概述
    ALTERA DDRII IP核使用
    MyEclipse的使用
    Java开发API文档资源
  • 原文地址:https://www.cnblogs.com/qinguoyi/p/10235202.html
Copyright © 2011-2022 走看看