对象的初始化和清理

  • 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全
  • C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置。

    构造函数和析构函数

    对象的初始化和清理也是两个非常重要的安全问题
    一个对象或者变量没有初始状态,对其使用后果是未知
    同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
    c++利用了构造函数析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。
    对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供
    编译器提供的构造函数和析构函数是空实现。

  • 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

  • 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。

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

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

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

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

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

    构造函数的分类及调用

    两种分类方式:
    按参数分为: 有参构造和无参构造
    按类型分为: 普通构造和拷贝构造
    三种调用方式:
    括号法
    显示法
    隐式转换法
    示例:

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

    拷贝构造函数调用时机

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

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

示例:

  1. class Person {
  2. public:
  3. Person() {
  4. cout << "无参构造函数!" << endl;
  5. mAge = 0;
  6. }
  7. Person(int age) {
  8. cout << "有参构造函数!" << endl;
  9. mAge = age;
  10. }
  11. Person(const Person& p) {
  12. cout << "拷贝构造函数!" << endl;
  13. mAge = p.mAge;
  14. }
  15. //析构函数在释放内存之前调用
  16. ~Person() {
  17. cout << "析构函数!" << endl;
  18. }
  19. public:
  20. int mAge;
  21. };
  22. //1. 使用一个已经创建完毕的对象来初始化一个新对象
  23. void test01() {
  24. Person man(100); //p对象已经创建完毕
  25. Person newman(man); //调用拷贝构造函数
  26. Person newman2 = man; //拷贝构造
  27. //Person newman3;
  28. //newman3 = man; //不是调用拷贝构造函数,赋值操作
  29. }
  30. //2. 值传递的方式给函数参数传值
  31. //相当于Person p1 = p;
  32. void doWork(Person p1) {}
  33. void test02() {
  34. Person p; //无参构造函数
  35. doWork(p);
  36. }
  37. //3. 以值方式返回局部对象
  38. Person doWork2()
  39. {
  40. Person p1;
  41. cout << (int *)&p1 << endl;
  42. return p1;
  43. }
  44. void test03()
  45. {
  46. Person p = doWork2();
  47. cout << (int *)&p << endl;
  48. }
  49. int main() {
  50. //test01();
  51. //test02();
  52. test03();
  53. system("pause");
  54. return 0;
  55. }
  1. #include <iostream>
  2. #include <string>
  3. using namespace std;
  4. class Test {
  5. public:
  6. Test() {
  7. m_Age = 0;
  8. cout << "Default Function has been Constructed!" << endl;
  9. }
  10. Test(int i_Age) {
  11. cout << "Parametric Function Constructed!" << endl;
  12. m_Age = i_Age;
  13. }
  14. //Try to delete this copy constructed function
  15. Test(const Test& t) {
  16. cout << "Copy Function has been Constructed!" << endl;
  17. m_Age = t.m_Age;
  18. }
  19. ~Test() {
  20. cout << "Function has been Destructed!" << endl;
  21. }
  22. void GetData(){
  23. cout << m_Age << endl;
  24. }
  25. int m_Age;
  26. };
  27. int main()
  28. {
  29. Test t1;
  30. t1.m_Age = 20;
  31. Test t2(t1);
  32. t2.GetData();
  33. t1.GetData();
  34. t2.m_Age = 30;
  35. t2.GetData();
  36. t1.GetData();
  37. return 0;
  38. }

构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:

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

示例:

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