zoukankan      html  css  js  c++  java
  • C++面向对象基础--对象的初始化和清理

    1.构造函数和析构函数

    对象的初始化和清理是两个非常重要的安全问题

    一个对象或者变量没有初始状态,对其使用后果是未知

    ​同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题

    C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作

    对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供

    编译器提供的构造函数和析构函数是空实现。 

    构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
    析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。 

    构造函数语法:类名(){}

    1. 构造函数,没有返回值也不写void
    2. 函数名称与类名相同
    3. 构造函数可以有参数,因此可以发生重载
    4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

    析构函数语法:~类名(){}

    1. 析构函数,没有返回值也不写void
    2. 函数名称与类名相同,在名称前加上符号 ~
    3. 析构函数不可以有参数,因此不可以发生重载
    4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

    示例代码:

     1 #include<iostream>
     2 using namespace std;
     3 class Person
     4 {
     5 public:
     6     //构造函数
     7     Person()
     8     {
     9         cout << "Person的构造函数调用" << endl;
    10     }
    11     //析构函数
    12     ~Person()
    13     {
    14         cout << "Person的析构函数调用" << endl;
    15     }
    16 
    17 };
    18 
    19 void test01()
    20 {
    21     Person p;
    22 }
    23 
    24 int main()
    25 {
    26 
    27     test01();
    28 
    29     system("pause");
    30 
    31     return 0;
    32 }

    运行结果:

    2.构造函数的分类及调用

    两种分类方式:

    ​按参数分为: 有参构造和无参构造

    按类型分为: 普通构造和拷贝构造

    三种调用方式:

    ​ (1)括号法

    ​ (2)显示法

    ​ (3)隐式转换法

     1 #include<iostream>
     2 using namespace std;
     3 //1、构造函数分类
     4 // 按照参数分类分为 有参和无参构造   无参又称为默认构造函数
     5 // 按照类型分类分为 普通构造和拷贝构造
     6 
     7 class Person
     8 {
     9 public:
    10     //无参(默认)构造函数
    11     Person()
    12     {
    13         cout << "无参构造函数!" << endl;
    14     }
    15     //有参构造函数
    16     Person(int a) 
    17     {
    18         age = a;
    19         cout << "有参构造函数!" << endl;
    20     }
    21     //拷贝构造函数
    22     Person(const Person& p) 
    23     {
    24         age = p.age;
    25         cout << "拷贝构造函数!" << endl;
    26     }
    27     //析构函数
    28     ~Person()
    29     {
    30         cout << "析构函数!" << endl;
    31     }
    32 public:
    33     int age;
    34 };
    35 
    36 //2、构造函数的调用
    37 //调用无参构造函数
    38 void test01()
    39 {
    40     Person p; //调用无参构造函数
    41 }
    42 
    43 //调用有参的构造函数
    44 void test02() 
    45 {
    46 
    47     //2.1  括号法,常用
    48     Person p1(10);
    49     //注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
    50     //Person p2();
    51 
    52     //2.2 显式法
    53     Person p2 = Person(10);
    54     Person p3 = Person(p2);
    55     //Person(10)单独写就是匿名对象  当前行结束之后,马上析构
    56 
    57     //2.3 隐式转换法
    58     Person p4 = 10; // Person p4 = Person(10); 
    59     Person p5 = p4; // Person p5 = Person(p4); 
    60 
    61     //注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
    62     //Person p5(p4);
    63 }
    64 
    65 int main() 
    66 {
    67 
    68     test01();
    69     //test02();
    70 
    71     system("pause");
    72 
    73     return 0;
    74 }

    运行结果:

    3.拷贝构造函数调用时机

    C++中拷贝构造函数调用时机通常有三种情况

    (1)使用一个已经创建完毕的对象来初始化一个新对象
    (2)值传递的方式给函数参数传值
    (3)以值方式返回局部对象

    示例代码:

     1 #include<iostream>
     2 using namespace std;
     3 class Person
     4 {
     5 public:
     6     Person()
     7     {
     8         cout << "无参构造函数!" << endl;
     9         mAge = 0;
    10     }
    11     Person(int age)
    12     {
    13         cout << "有参构造函数!" << endl;
    14         mAge = age;
    15     }
    16     Person(const Person& p) 
    17     {
    18         cout << "拷贝构造函数!" << endl;
    19         mAge = p.mAge;
    20     }
    21     //析构函数在释放内存之前调用
    22     ~Person() 
    23     {
    24         cout << "析构函数!" << endl;
    25     }
    26 public:
    27     int mAge;
    28 };
    29 
    30 //1. 使用一个已经创建完毕的对象来初始化一个新对象
    31 void test01() 
    32 {
    33 
    34     Person man(100); //p对象已经创建完毕
    35     Person newman(man); //调用拷贝构造函数
    36     Person newman2 = man; //拷贝构造
    37 
    38     //Person newman3;
    39     //newman3 = man; //不是调用拷贝构造函数,赋值操作
    40 }
    41 
    42 //2. 值传递的方式给函数参数传值
    43 //相当于Person p1 = p;
    44 void doWork(Person p1) {}
    45 void test02()
    46 {
    47     Person p; //无参构造函数
    48     doWork(p);
    49 }
    50 
    51 //3. 以值方式返回局部对象
    52 Person doWork2()
    53 {
    54     Person p1;
    55     cout << (int*)&p1 << endl;
    56     return p1;
    57 }
    58 
    59 void test03()
    60 {
    61     Person p = doWork2();
    62     cout << (int*)&p << endl;
    63 }
    64 
    65 
    66 int main() 
    67 {
    68 
    69     //test01();
    70     //test02();
    71     test03();
    72 
    73     system("pause");
    74 
    75     return 0;
    76 }

    运行结果:

    4.构造函数调用规则

    默认情况下,c++编译器至少给一个类添加3个函数

    1.默认构造函数(无参,函数体为空)

    2.默认析构函数(无参,函数体为空)

    3.默认拷贝构造函数,对属性进行值拷贝

    构造函数调用规则如下:

    如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造

    如果用户定义拷贝构造函数,c++不会再提供其他构造函数

    示例代码:

     1 #include<iostream>
     2 using namespace std;
     3 class Person
     4 {
     5 public:
     6     //无参(默认)构造函数
     7     Person()
     8     {
     9         cout << "无参构造函数!" << endl;
    10     }
    11     //有参构造函数
    12     Person(int a)
    13     {
    14         age = a;
    15         cout << "有参构造函数!" << endl;
    16     }
    17     //拷贝构造函数
    18     Person(const Person& p)
    19     {
    20         age = p.age;
    21         cout << "拷贝构造函数!" << endl;
    22     }
    23     //析构函数
    24     ~Person()
    25     {
    26         cout << "析构函数!" << endl;
    27     }
    28 public:
    29     int age;
    30 };
    31 
    32 void test01()
    33 {
    34     Person p1(18);
    35     //如果不写拷贝构造,编译器会自动添加拷贝构造,并且做浅拷贝操作
    36     Person p2(p1);
    37 
    38     cout << "p2的年龄为: " << p2.age << endl;
    39 }
    40 
    41 void test02()
    42 {
    43     //如果用户提供有参构造,编译器不会提供默认构造,会提供拷贝构造
    44     Person p1; //此时如果用户自己没有提供默认构造,会出错
    45     Person p2(10); //用户提供的有参
    46     Person p3(p2); //此时如果用户没有提供拷贝构造,编译器会提供
    47 
    48     //如果用户提供拷贝构造,编译器不会提供其他构造函数
    49     Person p4; //此时如果用户自己没有提供默认构造,会出错
    50     Person p5(10); //此时如果用户自己没有提供有参,会出错
    51     Person p6(p5); //用户自己提供拷贝构造
    52 }
    53 
    54 int main()
    55 {
    56 
    57     test01();
    58 
    59     system("pause");
    60 
    61     return 0;
    62 }

    运行结果:

    5.深拷贝与浅拷贝

    浅拷贝:简单的赋值拷贝操作

    深拷贝:在堆区重新申请空间,进行拷贝操作

    示例代码:

     1 #include<iostream>
     2 using namespace std;
     3 class Person 
     4 {
     5 public:
     6     //无参(默认)构造函数
     7     Person() 
     8     {
     9         cout << "无参构造函数!" << endl;
    10     }
    11     //有参构造函数
    12     Person(int age, int height)
    13     {
    14 
    15         cout << "有参构造函数!" << endl;
    16 
    17         m_age = age;
    18         m_height = new int(height);
    19 
    20     }
    21     //拷贝构造函数  
    22     Person(const Person& p) 
    23     {
    24         cout << "拷贝构造函数!" << endl;
    25         //如果不利用深拷贝在堆区创建新内存,会导致浅拷贝带来的重复释放堆区问题
    26         m_age = p.m_age;
    27         m_height = new int(*p.m_height);
    28 
    29     }
    30 
    31     //析构函数
    32     ~Person() 
    33     {
    34         cout << "析构函数!" << endl;
    35         if (m_height != NULL)
    36         {
    37             delete m_height;
    38         }
    39     }
    40 public:
    41     int m_age;
    42     int* m_height;
    43 };
    44 
    45 void test01()
    46 {
    47     Person p1(18, 180);
    48 
    49     Person p2(p1);
    50 
    51     cout << "p1的年龄: " << p1.m_age << " 身高: " << *p1.m_height << endl;
    52 
    53     cout << "p2的年龄: " << p2.m_age << " 身高: " << *p2.m_height << endl;
    54 }
    55 
    56 int main() 
    57 {
    58 
    59     test01();
    60 
    61     system("pause");
    62 
    63     return 0;
    64 }

    运行结果:

    总结:如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

    6.初始化列表

    作用:

    C++提供了初始化列表语法,用来初始化属性

    语法:构造函数():属性1(值1),属性2(值2)... {}

    示例代码:

     1 #include<iostream>
     2 using namespace std;
     3 class Person 
     4 {
     5 public:
     6 
     7     ////传统方式初始化
     8     //Person(int a, int b, int c)
     9     //{
    10     //    m_A = a;
    11     //    m_B = b;
    12     //    m_C = c;
    13     //}
    14 
    15     //初始化列表方式初始化
    16     Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) {}
    17     void PrintPerson() 
    18     {
    19         cout << "mA:" << m_A << endl;
    20         cout << "mB:" << m_B << endl;
    21         cout << "mC:" << m_C << endl;
    22     }
    23 private:
    24     int m_A;
    25     int m_B;
    26     int m_C;
    27 };
    28 
    29 int main()
    30 {
    31 
    32     Person p(1, 2, 3);
    33     p.PrintPerson();
    34 
    35 
    36     system("pause");
    37 
    38     return 0;
    39 }

    运行结果:

    7.类对象作为类成员

    C++类中的成员可以是另一个类的对象,我们称该成员为对象成员

    例如:

    1 class A {}
    2 class B
    3 {
    4     A a;
    5 }

    B类中有对象A作为成员,A为对象成员

    示例代码:

     1 #include<iostream>
     2 using namespace std;
     3 class Phone
     4 {
     5 public:
     6     Phone(string name)
     7     {
     8         m_PhoneName = name;
     9         cout << "Phone构造" << endl;
    10     }
    11 
    12     ~Phone()
    13     {
    14         cout << "Phone析构" << endl;
    15     }
    16 
    17     string m_PhoneName;
    18 
    19 };
    20 
    21 
    22 class Person
    23 {
    24 public:
    25 
    26     //初始化列表可以告诉编译器调用哪一个构造函数
    27     Person(string name, string pName) :m_Name(name), m_Phone(pName)
    28     {
    29         cout << "Person构造" << endl;
    30     }
    31 
    32     ~Person()
    33     {
    34         cout << "Person析构" << endl;
    35     }
    36 
    37     void playGame()
    38     {
    39         cout << m_Name << " 使用" << m_Phone.m_PhoneName << " 牌手机! " << endl;
    40     }
    41 
    42     string m_Name;
    43     Phone m_Phone;
    44 
    45 };
    46 void test01()
    47 {
    48     //当类中成员是其他类对象时,我们称该成员为 对象成员
    49     //构造的顺序是 :先调用对象成员的构造,再调用本类构造
    50     //析构顺序与构造相反
    51     Person p("张三", "苹果X");
    52     p.playGame();
    53 
    54 }
    55 
    56 
    57 int main()
    58 {
    59 
    60     test01();
    61 
    62     system("pause");
    63 
    64     return 0;
    65 }

    运行结果:

    8.静态成员

    静态成员就是在成员变量成员函数前加上关键字static,称为静态成员

    静态成员分为静态成员变量和静态成员函数

    静态成员变量包括:

    (1)所有对象共享同一份数据
    (2)在编译阶段分配内存
    (3)类内声明,类外初始化
    静态成员函数包括:
    (1)所有对象共享同一个函数
    (2)静态成员函数只能访问静态成员变量

    示例代码(静态成员变量):

     1 #include<iostream>
     2 using namespace std;
     3 class Person
     4 {
     5 public:
     6 
     7     static int m_A; //静态成员变量
     8 
     9     //静态成员变量特点:
    10     //1 在编译阶段分配内存
    11     //2 类内声明,类外初始化
    12     //3 所有对象共享同一份数据
    13 
    14 private:
    15     static int m_B; //静态成员变量也是有访问权限的
    16 };
    17 int Person::m_A = 10;
    18 int Person::m_B = 10;
    19 
    20 void test01()
    21 {
    22     //静态成员变量两种访问方式
    23 
    24     //1、通过对象
    25     Person p1;
    26     p1.m_A = 100;
    27     cout << "p1.m_A = " << p1.m_A << endl;
    28 
    29     Person p2;
    30     p2.m_A = 200;
    31     cout << "p1.m_A = " << p1.m_A << endl; //共享同一份数据
    32     cout << "p2.m_A = " << p2.m_A << endl;
    33 
    34     //2、通过类名
    35     cout << "m_A = " << Person::m_A << endl;
    36 
    37 
    38     //cout << "m_B = " << Person::m_B << endl; //私有权限访问不到
    39 }
    40 
    41 int main() 
    42 {
    43 
    44     test01();
    45 
    46     system("pause");
    47 
    48     return 0;
    49 }

    运行结果:

    示例代码(静态成员函数):

     1 #include<iostream>
     2 using namespace std;
     3 class Person
     4 {
     5 public:
     6 
     7     //静态成员函数特点:
     8     //1 程序共享一个函数
     9     //2 静态成员函数只能访问静态成员变量
    10 
    11     static void func()
    12     {
    13         cout << "func调用" << endl;
    14         m_A = 100;
    15         //m_B = 100; //错误,不可以访问非静态成员变量
    16     }
    17 
    18     static int m_A; //静态成员变量
    19     int m_B; // 
    20 private:
    21 
    22     //静态成员函数也是有访问权限的
    23     static void func2()
    24     {
    25         cout << "func2调用" << endl;
    26     }
    27 };
    28 int Person::m_A = 10;
    29 
    30 
    31 void test01()
    32 {
    33     //静态成员变量两种访问方式
    34 
    35     //1、通过对象
    36     Person p1;
    37     p1.func();
    38 
    39     //2、通过类名
    40     Person::func();
    41 
    42 
    43     //Person::func2(); //私有权限访问不到
    44 }
    45 
    46 int main() 
    47 {
    48 
    49     test01();
    50 
    51     system("pause");
    52 
    53     return 0;
    54 }

    运行结果:

  • 相关阅读:
    [LeetCode] 330. Patching Array 数组补丁
    [LeetCode] 875. Koko Eating Bananas 可可吃香蕉
    [LeetCode] 460. LFU Cache 最近最不常用页面置换缓存器
    [LeetCode] 395. Longest Substring with At Least K Repeating Characters 至少有K个重复字符的最长子字符串
    [LeetCode] 29. Divide Two Integers 两数相除
    [LeetCode] 451. Sort Characters By Frequency 根据字符出现频率排序
    [LeetCode] 296. Best Meeting Point 最佳开会地点
    [LeetCode] 317. Shortest Distance from All Buildings 建筑物的最短距离
    css3动画4
    CSS Transform / Transition / Animation 属性的区别
  • 原文地址:https://www.cnblogs.com/guanrongda-KaguraSakura/p/13385189.html
Copyright © 2011-2022 走看看