zoukankan      html  css  js  c++  java
  • 学习构造函数、拷贝构造函数、析构函数和重载运算符

    练习代码:

      1 #include <stdlib.h>
      2 #include <string>
      3 
      4 class Something
      5 {
      6 private:
      7     char* name;
      8     int weight;
      9 public:
     10     Something(){
     11         printf("调用了无参构造函数!
    ");
     12         weight = 0; 
     13         name = NULL;
     14     }
     15     Something(int w, const char* str = NULL)
     16     {        
     17         printf("调用了带参构造函数, name=%s, weight=%d!
    ", str, w);
     18 //        Something();                    // 调用上一级构造函数,初始化weight,name等变量,疑问:这一步没有起到效果,似乎不能这么调用,出现了name没有被初始化的错误
     19         weight = w;
     20         // 这里不需要提前释放name空间,因为name刚刚被构造
     21 
     22         if (str)
     23         {
     24             // 注意,此处必须 strlen(src)+1 ,因为还有足够的空间放下''字符
     25             // 如果没有+1,会出现不可预期的结果,甚至访问越界
     26             int len = strlen(str)+1;
     27             name = (char*)malloc(len);
     28             memcpy(name, str, len);
     29         }else
     30             name = NULL;
     31     }
     32     // 拷贝构造函数
     33     // 拷贝构造函数往往会在传参或返回的时候被调用:
     34     // 例如void func(Something s){},在构造s的时候,会调用拷贝构造函数
     35     // 例如Something func(){return *this;},在构造返回值时,也会调用拷贝构造函数
     36     // 在声明对象时:Something s = something;
     37     // 在声明对象时:Something s(something);
     38     Something(const Something& s)
     39     {
     40         printf("调用了拷贝构造函数, name=%s, weight=%d!
    ", s.name, s.weight);
     41 //        Something(s.weight+1, s.name);    // 调用上一级构造函数,疑问:这一步没有起到效果,似乎不能这么调用
     42         weight = s.weight+1;
     43         // 这里不需要提前释放name空间,因为name刚刚被构造
     44 
     45         if (s.name)                        
     46         {
     47             // 注意,此处必须 strlen(src)+1 ,因为还有足够的空间放下''字符
     48             // 如果没有+1,会出现不可预期的结果,甚至访问越界
     49             int len = strlen(s.name)+1;
     50             name = (char*)malloc(len);
     51             memcpy(name, s.name, len);
     52         }else
     53             name = NULL;                
     54     }
     55     ~Something()
     56     {
     57         if (name)
     58         {
     59             printf("调用了析构函数, name=%s, weight=%d!
    ", name, weight);
     60             free(name);
     61         }else
     62             printf("调用了析构函数, name=(null), weight=%d!
    ", weight);
     63     }
     64 
     65     // 非const:自己可以被修改,例如 (a = b) = c; 这种操作有效,结果是对a赋予c的值
     66     // 返回引用,避免重复构造对象,同时,自身可被修改
     67     // 传入的参数最好是引用类型,否则会调用拷贝构造函数,没必要
     68     // 赋值运算不会在声明对象的时候调用,声明对象的时候会调用拷贝构造函数,而不是赋值运算符
     69     // 因此左值都是已经初始化过的对象,在这里,其name一定是初始化过的,因此,有必要释放name所指内存
     70     //Something& operator= (const Something& s)
     71     Something& operator= (const Something& s)
     72     {
     73         printf("调用了赋值运算函数, name=%s!
    ", s.name);
     74         weight = s.weight;
     75         if (!name)
     76         {
     77             free(name);            // 有必要释放name所指内存
     78             name = NULL;
     79         }
     80         if(s.name)
     81         {
     82             // 注意,此处必须 strlen(src)+1 ,因为还有足够的空间放下''字符
     83             // 如果没有+1,会出现不可预期的结果,甚至访问越界
     84             int len = strlen(s.name)+1;
     85             name = (char*)malloc(len);
     86             memcpy(name, s.name, len);
     87         }
     88 
     89         return *this;
     90     }
     91     // 重载前置++运算符,完成:++Something
     92     Something& operator++ ()
     93     {
     94         ++weight;
     95         return *this;
     96     }
     97 
     98     // 重置后置++运算符,完成:Something++
     99     Something operator++(int i)
    100     {
    101         Something tmp = *this;    // 调用拷贝构造函数初始化tmp
    102         ++*this;
    103         return tmp;                // 调用拷贝构造函数初始化返回值对象,随后析构tmp
    104     }
    105 
    106     // b + c运算的返回值为const类型,可以避免b + c = a这种无效赋值操作
    107     // 由于返回的是临时对象,所以返回值不能是引用,否则会出现指向无效栈空间的BUG,导致不可预期的结果
    108     Something operator+ (const Something& s)
    109     {
    110         // 此处返回临时对象,且返回值类型不是引用,所以会调用构造函数
    111         //Something tmp(weight+s.weight);        // 构造tmp
    112         //return tmp;                            // 拷贝构造返回对象,语句结束时析构tmp
    113         return Something(weight+s.weight);
    114     }
    115         
    116     void setName(const char* str)
    117     {
    118         if(!name)
    119         {
    120             free(name);
    121             name = NULL;
    122         }
    123         if(str)
    124         {
    125             // 注意,此处必须 strlen(src)+1 ,因为还有足够的空间放下''字符
    126             // 如果没有+1,会出现不可预期的结果,甚至访问越界
    127             int len = strlen(str)+1;
    128             name = (char*)malloc(len);
    129             memcpy(name, str, len);
    130         }
    131     }
    132     void toPrint()
    133     {
    134         printf("name=%s, weight = %d
    ", name, weight);
    135     }
    136 };
    137 
    138 Something foo(Something s)            // 因为参数不是引用类型,所以此处会调用拷贝构造函数
    139 {
    140     s.toPrint();
    141     s.setName("s6");
    142     return s;                        // 此处会调用s的拷贝构造,产生一个返回对象,然后调用s的析构函数
    143 }
    144 
    145 int main()
    146 {
    147 
    148     Something s1(1, "s1"), s2(2, "s2");
    149     s1.toPrint();
    150     s2.toPrint();
    151     s1+s2;                        // 构造返回的对象,本语句结束时析构返回的对象
    152     Something s3 = s1+s2;        // 为何此处没有调用拷贝构造函数?也没有调用赋值运算,也没调用析构函数析构返回对象,仅仅调用一个构造函数
    153                                 // 一般的声明赋初值操作会调用拷贝构造函数,这里没有调用拷贝构造函数
    154                                 // 一般的函数,返回的对象会被析构,这里没有析构
    155                                 // 暂认为是编译器在此做了优化,直接构造了s3,相当于优化成了 Something s3(s1.weight+s2.weight);
    156     
    157     s3.setName("s3");
    158     s3.toPrint();
    159     Something s4 = s3;            // 此处会调用拷贝构造函数
    160     s4.setName("s4");
    161     s4.toPrint();
    162     Something s5(s4);            // 此处会调用拷贝构造函数
    163     (s5 = s4) = s1;                // 此处会调用两次赋值运算符,略奇葩的赋值,合法,但是没啥特殊意义,等同于 s5 = s1
    164     s5.setName("s5");
    165     s5.toPrint();
    166     Something s6 = foo(s5);        // 函数返回一个Something对象,调用拷贝构造函数,表达式结束后,返回的对象被析构
    167     s6++;                        // 返回值都是非引用类型,操作符返回一个SoSomething对象,随后被析构
    168     s6.toPrint();
    169     ++s6;                        // 参数和返回值均为引用类型,不调用任何构造、拷贝构造和析构函数
    170     s6.toPrint();
    171     
    172     getchar();
    173     return 0;
    174 }

    输出结果:

    调用了带参构造函数, name=s1, weight=1!
    调用了带参构造函数, name=s2, weight=2!
    name=s1, weight = 1
    name=s2, weight = 2
    调用了带参构造函数, name=(null), weight=3!
    调用了析构函数, name=(null), weight=3!
    调用了带参构造函数, name=(null), weight=3!
    name=s3, weight = 3
    调用了拷贝构造函数, name=s3, weight=3!
    name=s4, weight = 4
    调用了拷贝构造函数, name=s4, weight=4!
    调用了赋值运算函数, name=s4!
    调用了赋值运算函数, name=s1!
    name=s5, weight = 1
    调用了拷贝构造函数, name=s5, weight=1!
    name=s5, weight = 2
    调用了拷贝构造函数, name=s6, weight=2!
    调用了析构函数, name=s6, weight=2!
    调用了拷贝构造函数, name=s6, weight=3!
    调用了拷贝构造函数, name=s6, weight=4!
    调用了析构函数, name=s6, weight=4!
    调用了析构函数, name=s6, weight=5!
    name=s6, weight = 4
    name=s6, weight = 5
  • 相关阅读:
    STL
    STL
    Python编程-基础知识-条件判断
    STL
    springmvc 自定义注解
    Springboot 入口类及其实现自动配置的原理
    Java RestTemplate post请求传递参数遇到的坑
    Spring中@Autowire的底层原理解析(附详细源码阅读步骤)
    非常详细的SpringBoot-自动装配原理
    为何一个@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?
  • 原文地址:https://www.cnblogs.com/zanzan101/p/3332393.html
Copyright © 2011-2022 走看看