• 【c++】cpp类和对象


    (1)类的封装

    封装的多层含义

    • 把属性和⽅法进⾏封装
    • 对属性和⽅法进⾏访问控制
    • 类的内部和类的外部
    • 类的访问控制关键字
      • public:  修饰的成员变量和函数,可以在类的内部和类的外部访问
      • private:  修饰的成员变量和函数,只能在类的内部被访问,不能在类的外部访问
      • protected:  修饰的成员变量和函数,只能在类的内部被访问,不能在类的外部访问,⽤ 在继承⾥⾯

    案例:设计⽴⽅体类(cube),求出⽴⽅体的⾯积和体积。求两个⽴⽅体,是否相等(全局函数和成员函数都要实现)。

    1. #include
    2. class Cube
    3. {
    4. public:
    5. void setA(int a){m_a = a;}
    6. void setB(int b){m_b = b;}
    7. void setC(int c){m_c = c;}
    8. void setABC(int a = 0, int b = 0, int c = 0)
    9. {
    10. m_a = a;
    11. m_b = b;
    12. m_c = c;
    13. }
    14. int getA(){return m_a;}
    15. int getB(){return m_b;}
    16. int getC(){return m_c;}
    17. public:
    18. int getV()
    19. {
    20. m_v = m_a * m_b * m_c;
    21. return m_v;
    22. }
    23. int getS()
    24. {
    25. m_s = 2 * (m_a * m_b + m_a * m_c + m_b * m_c);
    26. return m_s;
    27. }
    28. int judgeCube(Cube &v1, Cube &v2) //不建议这样写
    29. {
    30. if ((v1.getA() == v2.getA())
    31. && (v1.getB() == v2.getB())
    32. && (v1.getC() == v2.getC()))
    33. return 1;
    34. return 0;
    35. }
    36. int judgeCube(Cube &v2) //成员函数 函数重载
    37. {
    38. if ((m_a == v2.getA())
    39. && (m_b == v2.getB())
    40. && (m_c == v2.getC()))
    41. return 1;
    42. return 0;
    43. }
    44. private:
    45. int m_a;
    46. int m_b;
    47. int m_c;
    48. int m_v;
    49. int m_s;
    50. };
    51. // 全局函数 PK 成员函数
    52. // 1相等 0不相等
    53. int judgeCube(Cube &v1, Cube &v2)
    54. {
    55. if ((v1.getA() == v2.getA())
    56. && (v1.getB() == v2.getB())
    57. && (v1.getC() == v2.getC()))
    58. return 1;
    59. return 0;
    60. }
    61. int main()
    62. {
    63. Cube v1, v2;
    64. v1.setABC(1, 2, 3);
    65. std::cout << v1.getS() << std::endl;
    66. std::cout << v1.getV() << std::endl;
    67. // 用成员函数判断两个立方体是否相等
    68. v2.setABC(1, 2, 4);
    69. if (v1.judgeCube(v1, v2) == 1)
    70. std::cout << "v1 = v2" << std::endl;
    71. else
    72. std::cout << "v1 != v2" << std::endl;
    73. // 用成员函数判断两个立方体是否相等
    74. v1.setABC(1, 2, 4);
    75. if (v1.judgeCube(v2) == 1)
    76. std::cout << "v1 = v2" << std::endl;
    77. else
    78. std::cout << "v1 != v2" << std::endl;
    79. // 用全局函数判断两个立方体是否相等
    80. if (judgeCube(v1, v2) == 1)
    81. std::cout << "v1 = v2" << std::endl;
    82. else
    83. std::cout << "v1 != v2" << std::endl;
    84. return 0;
    85. }

    (2)类的声明和实现分开

    设计.h和.cpp,一个声明,一个实现。为了避免同⼀个头⽂件被include多次带来重复编译,头⽂件要么用#ifndef 宏名  #define 宏名  #endif编写头文件,要么用#pragma once。#ifndef由语⾔⽀持移植性更好,推荐使用。

    案例:带有数组成员的类

    myarrray.h

    1. #ifndef _MYARRAY_H_
    2. #define _MYARRAY_H_
    3. class Array
    4. {
    5. public:
    6. Array(int length);
    7. Array(const Array &obj); //加了const更好
    8. ~Array();
    9. public:
    10. void setdata(int index, int value);
    11. int getdata(int index);
    12. int getLenth();
    13. private:
    14. int m_length;
    15. int *m_space;
    16. };
    17. #endif

    myarray.cpp

    1. #include
    2. #include "myarray.h"
    3. //作用域不要忘写
    4. //构造函数
    5. Array::Array(int length)
    6. {
    7. if (length < 0){length = 0;}
    8. m_length = length;
    9. m_space = new int[length];
    10. // this->m_length = length;
    11. // this->m_space = new int[length];
    12. }
    13. //构造函数的重载
    14. Array::Array(const Array &obj)
    15. {
    16. m_length = obj.m_length;
    17. m_space = new int[m_length];
    18. for (int i = 0; i < m_length; i++)
    19. m_space[i] = obj.m_space[i];
    20. }
    21. //析构函数
    22. Array::~Array()
    23. {
    24. if (m_space != NULL)
    25. {
    26. delete[] m_space;
    27. m_space = NULL;
    28. m_length = -1;
    29. }
    30. }
    31. //成员函数
    32. void Array::setdata(int index, int value)
    33. {
    34. if (0 <= index && index <= m_length)
    35. m_space[index] = value;
    36. }
    37. int Array::getdata(int index)
    38. {
    39. if (0 <= index && index <= m_length)
    40. return m_space[index];
    41. return -1;
    42. }
    43. int Array::getLenth()
    44. {
    45. return m_length;
    46. }

    main.cpp

    1. #include
    2. #include "myarray.h"
    3. int main()
    4. {
    5. Array array1(4);
    6. for (int i = 0; i < array1.getLenth(); i++)
    7. {
    8. array1.setdata(i, i + 100);
    9. std::cout << array1.getdata(i) << "\t";
    10. }
    11. std::cout << std::endl;
    12. Array array2 = array1;
    13. for (int i = 0; i < array2.getLenth(); i++)
    14. {
    15. std::cout << array2.getdata(i) << "\t";
    16. }
    17. std::cout << std::endl;
    18. return 0;
    19. }

    编译运行

    1. [root@centos1 test]# g++ -o main main.cpp myarray.cpp
    2. [root@centos1 test]# ./main
    3. 100 101 102 103
    4. 100 101 102 103
    5. [root@centos1 test]#

    (3)对象的构造和析构

    定义和调用说明

    1 构造函数定义

    • C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数。
    • 构造函数默认是无参数的,当类中没有定义构造函数时,编译器默认提供⼀个⽆参构造函数, 并且其函数体为空。也可以在定义时自行设计有参数和函数体。
    • 没有任何返回类型的声明。

    2 构造函数的调⽤

    • ⾃动调⽤:⼀般情况下C++编译器会⾃动调⽤构造函数
    • ⼿动调⽤:在⼀些情况下则需要⼿⼯调⽤构造函数

    3 析构函数定义及调⽤

    • C++中的类可以定义⼀个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数
    • 语法:~ClassName()
    • 析构函数没有参数也没有任何返回类型的声明
    • 析构函数在对象销毁时⾃动被调⽤
    • 析构函数调⽤机制:C++编译器⾃动调⽤

    有参数的构造函数,构造函数重载,拷贝构造函数

    ⼆个特殊的构造函数:

    • 默认无参构造函数。当类中没有定义构造函数时,编译器默认提供⼀个⽆参构造函数, 并且其函数体为空。
    • 默认拷贝构造函数。当类中没有定义拷⻉构造函数时,编译器默认提供⼀个默认拷⻉构造函数,简单的进⾏成员变量的值复制。

    示例代码

    1. #include
    2. #include
    3. class Test
    4. {
    5. public:
    6. Test() //无参数构造函数
    7. {
    8. age = 0;
    9. strcpy(name, "tom");
    10. }
    11. Test(int age) //有参数构造函数 //构造函数重载
    12. {
    13. this->age = age;
    14. strcpy(name, "tom");
    15. }
    16. Test(int age, char *name) //有参数构造函数 //构造函数重载
    17. {
    18. this->age = age;
    19. strcpy(this->name, name);
    20. }
    21. Test(const Test &obj) // 拷贝构造函数
    22. {
    23. this->age = obj.age;
    24. this->name = obj.name;
    25. }
    26. ~Test() //析构函数
    27. {
    28. std::cout << "object destroyed" << std::endl;
    29. }
    30. public:
    31. void printT()
    32. {
    33. std::cout << "name = " << name << std::endl;
    34. std::cout << "age = " << age << std::endl;
    35. std::cout << "---------------" << std::endl;
    36. }
    37. private:
    38. char *name = new char[20];
    39. int age;
    40. };
    41. int main()
    42. {
    43. // 1.c++编译器自动的调用构造函数
    44. Test t0; // 自动调用无参数构造函数
    45. t0.printT();
    46. char name1[] = "hello";
    47. Test t1(10, name1); // 自动调用参数构造函数
    48. t1.printT();
    49. // 2.手动调用构造函数
    50. char name2[] = "world";
    51. Test t2 = Test(100, name2);
    52. t2.printT();
    53. strcpy(name2, "good");
    54. Test t3 = Test(1000, name2); //t4对象的初始化
    55. t2 = t3;
    56. t2.printT();
    57. // 3.对象赋值
    58. t0 = t1;
    59. std::cout << &t0 << "," << &t1 << std::endl; // 不同
    60. t0.printT();
    61. //对象的初始化 和 对象的赋值 是两个不同的概念
    62. // 4.拷贝构造函数
    63. Test t4 = Test(t3);
    64. //Test t4 = t3; // 等价的方式 // 对象的初始化
    65. t4.printT();
    66. std::cout << &t3 << "," << &t4 << std::endl; // 不同
    67. return 0;
    68. }

    编译运行

    1. [root@centos1 test]# g++ main.cpp -o main -std=c++11
    2. [root@centos1 test]# ./main
    3. name = tom
    4. age = 0
    5. ---------------
    6. name = hello
    7. age = 10
    8. ---------------
    9. name = world
    10. age = 100
    11. ---------------
    12. name = good
    13. age = 1000
    14. ---------------
    15. 0x7ffd41eaa4e0,0x7ffd41eaa4c0
    16. name = hello
    17. age = 10
    18. ---------------
    19. name = good
    20. age = 1000
    21. ---------------
    22. 0x7ffd41eaa490,0x7ffd41eaa480
    23. object destroyed
    24. object destroyed
    25. object destroyed
    26. object destroyed
    27. object destroyed
    28. [root@centos1 test]#

    匿名对象的去和留

    可以用函数返回对象,为匿名对象。关于匿名对象的去和留,结论如下:

    • 返回的匿名对象,若没人接盘,则这个匿名对象构造后会直接析构。
    • 返回的匿名对象,若赋值给另外一个同类型的对象,那么匿名对象在构造后会被析构。
    • 返回的匿名对象,若直接来初始化另外一个同类型的对象,那么匿名对象会直接转成新的对象。

    示例代码

    1. #include
    2. class Location
    3. {
    4. public:
    5. Location(int xx = 0, int yy = 0)
    6. {
    7. X = xx;
    8. Y = yy;
    9. std::cout << "Object Created\n";
    10. }
    11. Location(const Location &p) //拷贝构造函数
    12. {
    13. X = p.X;
    14. Y = p.Y;
    15. std::cout << "Object Copy" << std::endl;
    16. }
    17. ~Location()
    18. {
    19. std::cout << "(" << X << "," << Y << ") "
    20. << "Object Destroyed" << std::endl;
    21. }
    22. public:
    23. int GetX() { return X; }
    24. int GetY() { return Y; }
    25. private:
    26. int X, Y;
    27. };
    28. void PrintLocation(Location p) //打印对象的属性
    29. {
    30. std::cout << "Funtion:" << p.GetX() << "," << p.GetY() << std::endl;
    31. }
    32. Location GenLocation() //返回一个匿名对象
    33. {
    34. Location A(1, 2);
    35. return A;
    36. }
    37. // 匿名对象的去和留,关键看,返回时如何接盘
    38. // = 在赋值和初始化这两种情况时有所不同
    39. int main()
    40. {
    41. // (1)若返回的匿名对象,赋值给另外一个同类型的对象,那么匿名对象会被析构
    42. std::cout << "--------B--------" << std::endl;
    43. Location B; // 调用一次构造函数
    44. B = GenLocation(); // 调用构造函数 // 然后调用析构函数
    45. //(2)若返回的匿名对象,直接来初始化另外一个同类型的对象
    46. // 那么匿名对象会直接转成新的对象
    47. std::cout << "--------C--------" << std::endl;
    48. Location C = GenLocation(); // 调用一次构造函数
    49. //(3)没人接盘 //这个匿名对象构造后会直接析构
    50. std::cout << "----------------" << std::endl;
    51. GenLocation(); // 调用一次构造函数 // 然后调用析构函数
    52. std::cout << "--------end--------" << std::endl;
    53. // 程序结束时
    54. // B调用一次析构函数
    55. // C调用一次析构函数
    56. return 0;
    57. }

    编译运行

    1. [root@localhost test]# g++ main.cpp -o main
    2. [root@localhost test]# ./main
    3. --------B--------
    4. Object Created
    5. Object Created
    6. (1,2) Object Destroyed
    7. --------C--------
    8. Object Created
    9. ----------------
    10. Object Created
    11. (1,2) Object Destroyed
    12. --------end--------
    13. (1,2) Object Destroyed
    14. (1,2) Object Destroyed
    15. [root@localhost test]#

    类中嵌套对象的初始化与对象初始化列表

    情况描述:如果我们设计的类中有一个成员,它本身是另一个类,而且这个成员构造函数只有带参数的构造函数,没有默认构造函数。这时要对这个类成员进行初始化,就必须调用这个类成员的带参构造函数,如果没有初始化列表,那么他将无法完成第一步,就会报错。

    解决办法:C++在构造函数中提供初始化列表对成员变量进行初始化。

    语法规则作用域::类名(参数1, 参数2, ...):成员类名1(实参值a, 实参值b,...), 成员类名2((实参值m, 实参值n,...)

    构造函数和析构函数调用顺序

    • 构造函数顺序:先执行被组合对象的构造函数 。如果组合对象有多个,按照定义顺序,,而不是按照初始化列表的顺序。初始化列表先于构造函数的函数体执行。
    • 析构函数 : 和构造函数的调用顺序相反

    示例:在B类中,组合了一个 A类对象,A类设计了构造函数,但构造函数只有带参的构造函数,根据构造函数的调用规则,必须要调用A类的带参构造函数。

    示例代码

    1. #include
    2. #include
    3. class A
    4. {
    5. public:
    6. A(int _a)
    7. {
    8. a = _a;
    9. std::cout << "generate A " << a << std::endl;
    10. }
    11. ~A()
    12. {
    13. std::cout << "destroy A " << a << std::endl;
    14. }
    15. protected:
    16. private:
    17. int a;
    18. };
    19. class B
    20. {
    21. public:
    22. //B(int _b1, int _b2): a1(_b1), a2(_b2)
    23. B(int _b1, int _b2): a1(1), a2(2)
    24. {
    25. b1 = _b1;
    26. b2 = _b2;
    27. std::cout << "generate B " << std::endl;
    28. }
    29. B(int _b1, int _b2, int m, int n):a1(m), a2(n)
    30. {
    31. b1 = _b1;
    32. b2 = _b2;
    33. std::cout << "generate B " << std::endl;
    34. }
    35. ~B()
    36. {
    37. std::cout << "destroy B " << std::endl;
    38. }
    39. protected:
    40. private:
    41. int b1;
    42. int b2;
    43. A a2;
    44. A a1;
    45. };
    46. void objplay()
    47. {
    48. B(1, 2);
    49. }
    50. int main()
    51. {
    52. objplay();
    53. return 0;
    54. }

    运行结果

    1. generate A 2
    2. generate A 1
    3. generate B
    4. destroy B
    5. destroy A 1
    6. destroy A 2

    类中const常量初始化与对象初始化列表

    当类成员中含有⼀个const对象时,或者是⼀个引⽤时,他们也必须要通过成员初始化列表进⾏初始化,因为这两种对象要在声明后⻢上初始化,⽽在构造函数中,做的是对他们的赋值,这样是不被允许的。

    示例代码

    1. #include
    2. #include
    3. class A
    4. {
    5. public:
    6. A(int _a)
    7. {
    8. a = _a;
    9. std::cout << "generate A " << a << std::endl;
    10. }
    11. ~A()
    12. {
    13. std::cout << "destroy A " << a << std::endl;
    14. }
    15. public:
    16. int geta(){return a;}
    17. protected:
    18. private:
    19. int a;
    20. };
    21. class B
    22. {
    23. public:
    24. B(int _b1, int _b2): a1(_b1), a2(_b2), c(_b2)
    25. // B(int _b1, int _b2): a1(1), a2(2), c(0)
    26. {
    27. b1 = _b1;
    28. b2 = _b2;
    29. std::cout << "generate B " << std::endl;
    30. }
    31. B(int _b1, int _b2, int m, int n):a1(m), a2(n), c(0)
    32. {
    33. b1 = _b1;
    34. b2 = _b2;
    35. std::cout << "generate B " << std::endl;
    36. }
    37. ~B()
    38. {
    39. std::cout << "destroy B " << std::endl;
    40. }
    41. public:
    42. void print()
    43. {
    44. std::cout << "---------" << std::endl;
    45. std::cout << b1 << std::endl;
    46. std::cout << b2 << std::endl;
    47. std::cout << a2.geta() << std::endl;
    48. std::cout << a1.geta() << std::endl;
    49. std::cout << c << std::endl;
    50. }
    51. protected:
    52. private:
    53. int b1;
    54. int b2;
    55. A a2;
    56. A a1;
    57. const int c;
    58. };
    59. void objplay()
    60. {
    61. B obj_b1 = B(1, 2);
    62. obj_b1.print();
    63. B obj_b2(3, 4);
    64. obj_b2.print();
    65. }
    66. int main()
    67. {
    68. objplay();
    69. return 0;
    70. }

    运行结果

    1. generate A 2
    2. generate A 1
    3. generate B
    4. ---------
    5. 1
    6. 2
    7. 2
    8. 1
    9. 2
    10. generate A 4
    11. generate A 3
    12. generate B
    13. ---------
    14. 3
    15. 4
    16. 4
    17. 3
    18. 4
    19. destroy B
    20. destroy A 3
    21. destroy A 4
    22. destroy B
    23. destroy A 1
    24. destroy A 2

    (4)new和delete用法

    在软件开发过程中,常常需要动态地分配和撤销内存空间。在C语⾔中是利⽤库函数malloc和 free来分配和撤销内存空间的。C++提供了较简便⽽功能较强的运算符new和delete来取代 malloc和free函数。

    注意:new和delete是运算符,不是函数,因此执⾏效率⾼。

    虽然为了与C语⾔兼容,C++仍保留malloc和free函数,但建议⽤户不⽤malloc和free函数,⽽ ⽤new和delete运算符。

    new用法举例

    • new int;  //开辟⼀个存放整数的存储空间,返回⼀个指向该存储空间的地址(即指针)
    • new int(100);  //开辟⼀个存放整数的空间,并指定该整数的初值为100,返回⼀个指向该 存储空间的地址
    • new char[10];  //开辟⼀个存放字符数组(包括10个元素)的空间,返回⾸元素的地址
    • new int[5][4];  //开辟⼀个存放⼆维整型数组(⼤⼩为5*4)的空间,返回⾸元素的地址
    • float *p=new float(3.14159);  //开辟⼀个存放单精度数的空间,并指定该实数的初值为 3.14159,将返回的该空间的地址赋给指针变量p
    • Box *pt;  //定义⼀个指向Box类对象的指针变量pt
    • pt=new Box;  //在pt中存放了新建对象的起始地址
    • Box *pt = new Box ;
    • Box *pt=new Box(12,15,18);

    注意⽤new分配数组空间时不能指定初值。如果由于内存不⾜等原因⽽⽆法正常分配空间,则new会 返回⼀个空指针NULL,⽤户可以根据该指针的值判断分配空间是否成功。

    delete用法

    若P是⼀个指向数组的指针,则delete p表示清空内存⾸地址,delete[] p表示清空整个数组 对应的堆中内存空间。

    malloc-free和new-delete区别

    • malloc不会调⽤类的构造函数,new能执⾏类型构造函数
    • free不会调⽤类的析构函数,delete操作符 能执⾏类的析构函数

    示例代码

    1. #include
    2. #include
    3. class Test
    4. {
    5. public:
    6. Test(int _a)
    7. {
    8. a = _a;
    9. std::cout << "generate A" << std::endl;
    10. }
    11. ~Test()
    12. {
    13. std::cout << "destroy A" << std::endl;
    14. }
    15. protected:
    16. private:
    17. int a;
    18. };
    19. int main()
    20. {
    21. // 1.基础类型
    22. // 用malloc这个函数
    23. int *p1 = (int *)malloc(sizeof(int));
    24. *p1 = 10;
    25. free(p1);
    26. // 用new这个运算符
    27. int *p2 = new int;
    28. *p2 = 20;
    29. free(p2);
    30. int *p3 = new int(30);
    31. delete p3;
    32. // 2.数组
    33. // malloc
    34. int *p4 = (int *)malloc(sizeof(int) * 10); // int array[10];
    35. p4[0] = 1;
    36. free(p4);
    37. // new
    38. int *pArray = new int[10];
    39. pArray[1] = 2;
    40. delete[] pArray;
    41. char *pArray2 = new char[25];
    42. delete[] pArray2;
    43. // 3.给对象分配堆内内存空间
    44. std::cout << "---------malloc------------" << std::endl;
    45. Test *testp1 = (Test *)malloc(sizeof(Test));
    46. std::cout << "---------free------------" << std::endl;
    47. free(testp1);
    48. std::cout << "----------new-----------" << std::endl;
    49. Test *testp2 = new Test(1);
    50. std::cout << "----------delete-----------" << std::endl;
    51. delete testp2;
    52. return 0;
    53. }

    运行结果

    1. ---------malloc------------
    2. ---------free------------
    3. ----------new-----------
    4. generate A
    5. ----------delete-----------
    6. destroy A

    (5)static与静态成员变量(函数)

    静态成员变量

    思考:每个变量,拥有属性。有没有⼀些属性,归所有对象拥有?

    把⼀个类的成员修饰为 static 时,这个类⽆论有多少个对象被创建,这些同类对象共享这个 static 成员。

    • 静态成员在类的内部声明,但要在类的外部初始化。静态成员局部于类,它不是对象成员。
    • 静态不初始化时,只要不调⽤这个静态成员编译就不会报错,但只有调⽤时才会报错。
    • 静态成员只有在初始化后,才会分配内存空间。
    1. #include
    2. class counter
    3. {
    4. public:
    5. static int num; //声明静态数据成员
    6. public:
    7. void setnum(int i) { num = i; } //成员函数访问静态数据成员
    8. void shownum() { std::cout << num << std::endl; }
    9. };
    10. // 在类的外部初始化静态数据成员
    11. // 只能初始化一次
    12. int counter::num = 0;
    13. int main()
    14. {
    15. counter a, b;
    16. //调用成员函数访问私有静态数据成员
    17. a.shownum(); //0
    18. b.shownum(); //0
    19. //只要有一个对象改变了静态成员的值
    20. //则所有对象调用该静态成员时的值也会变
    21. a.setnum(10);
    22. a.shownum(); //10
    23. b.shownum(); //10
    24. //若静态数据成员是public的
    25. //还可以直接通过类访问静态成员
    26. std::cout << counter::num << std::endl;
    27. return 0;
    28. }

    静态成员函数

    用static修饰成员函数,成为静态函数。

    对于静态成员函数,是全部对象公有函数,提供不依赖于类数据结构的共同操作,它没有this指针。

    • 在类外调⽤静态成员函数⽤ “类名::静态函数”作限定词,或通过对象调⽤。
    • 注意静态函数中,不能使用普通成员变量,也不能调用普通成员函数,只能使用静态成员变量

    1. #include
    2. class Worker
    3. {
    4. public:
    5. void company_age_add(){company_age++;}
    6. void print_company_age1()
    7. {
    8. std::cout << "normal print: company_age=" << company_age << std::endl;
    9. }
    10. static void print_company_age2() //静态成员函数
    11. {
    12. std::cout << "static print: company_age=" << company_age << std::endl;
    13. // std::cout << "age:" << age << std::endl;
    14. // error C2597: 对非静态成员“Worker::age”的非法引用
    15. // 静态函数中 不能使用 普通成员变量 普通成员函数
    16. }
    17. protected:
    18. private:
    19. int age;
    20. int salary;
    21. static int company_age; //静态成员变量
    22. };
    23. int Worker::company_age = 10;
    24. int main()
    25. {
    26. Worker w1, w2, w3;
    27. // 1.普通成员函数调用静态成员变量
    28. w1.print_company_age1(); // normal print: company_age=10
    29. w2.company_age_add();
    30. w3.print_company_age1(); // normal print: company_age=11
    31. // 2.调用静态成员函数
    32. // 第一种:用对象.
    33. w3.print_company_age2(); // static print: company_age=11
    34. // 第二种:类::
    35. Worker::print_company_age2(); // static print: company_age=11
    36. return 0;
    37. }

    (6)c++编译器对成员变量和函数的处理机制

    内存占用机制

    C++类对象中的成员变量和成员函数是分开存储的。

    • 成员变量:
      • 普通成员变量:存储于对象中,与struct变量有相同的内存布局和字节对⻬⽅式
      • 静态成员变量:存储于全局数据区中
    • 成员函数:存储于代码段中。
    1. #include
    2. #include
    3. class C1
    4. {
    5. public:
    6. int i; // 4个字节
    7. int j; // 4个字节
    8. int k; // 4个字节
    9. protected:
    10. private:
    11. }; //共12个字节
    12. class C2
    13. {
    14. public:
    15. int i;
    16. int j;
    17. int k;
    18. static int m;
    19. public:
    20. int getK() const { return k; }
    21. void setK(int val) { k = val; }
    22. protected:
    23. private:
    24. };
    25. struct S1
    26. {
    27. int i;
    28. int j;
    29. int k;
    30. }; // 12
    31. struct S2
    32. {
    33. int i;
    34. int j;
    35. int k;
    36. static int m;
    37. }; // 12
    38. int main()
    39. {
    40. printf("c1:%d \n", sizeof(C1)); //12
    41. printf("c2:%d \n", sizeof(C2)); //12
    42. printf("s1:%d \n", sizeof(S1)); //12
    43. printf("s2:%d \n", sizeof(S2)); //12
    44. return 0;
    45. }

    this指针

    C++中类的普通成员函数都隐式包含⼀个指向当前对象的this指针。比如类成员函数Test(int a, int b)其实是Test(Test *this, int a, int b)。

    静态成员函数与普通成员函数的区别:静态成员函数不包含指向具体对象的指针,普通成员函数 包含⼀个指向具体对象的指针。

    1. #include
    2. class Test
    3. {
    4. public:
    5. Test(int a, int b)
    6. {
    7. this->a = a;
    8. this->b = b;
    9. }
    10. void print1(int a) //这个a是函数形参的a
    11. {
    12. std::cout << "----------print1----------" << std::endl;
    13. for (int i = 0; i < a; i++)
    14. {
    15. std::cout << "a: " << this->a << std::endl; //打印对象的属性a的值
    16. std::cout << "b: " << this->b << std::endl;
    17. }
    18. }
    19. void print2()
    20. {
    21. std::cout << "----------print2----------" << std::endl;
    22. // 当函数形参没有与成员变量同名的形参时
    23. // 函数内部可以不用this,直接访问成员变量
    24. std::cout << "a: " << a << std::endl; //打印对象的属性a的值
    25. std::cout << "b: " << b << std::endl;
    26. }
    27. protected:
    28. private:
    29. int a;
    30. int b;
    31. };
    32. int main()
    33. {
    34. Test t1(1, 2);
    35. t1.print1(3);
    36. t1.print2();
    37. return 0;
    38. }

    ⽤const限定this指针

    C++中类的普通成员函数都隐式包含⼀个指向当前对象的this指针。比如类成员函数Test(int a, int b)其实是Test(Test *this, int a, int b)。

    将const写在成员函数处,比如void OperateVar(int a, int b) const,将会对this指针和this下的成员进行修饰,只有只读属性,使得this和this成员不能被改变。

    注意:

    • const 无论写在函数什么位置,都没有关系,像下面这样都可以
      • void OperateVar(int a, int b) const

      • const void OperateVar(int a, int b)

      • void const OperateVar(int a, int b)

    • const修饰的是谁?

      • const修饰的是形参a吗?不是

      • const修饰的是属性this->a  this->b

      • const修饰的是this指针所指向的内存空间, 修饰的是this指针

    1. #include
    2. class Test
    3. {
    4. public:
    5. Test(int a, int b) //---> Test(Test *this, int a, int b)
    6. {
    7. this->a = a;
    8. this->b = b;
    9. }
    10. void printT()
    11. {
    12. std::cout << "a: " << a << "\t";
    13. std::cout << "b: " << this->b << std::endl;
    14. }
    15. void OperateVar(int a, int b) const
    16. // void const OperateVar(int a, int b)
    17. // const void OperateVar(int a, int b)
    18. {
    19. a = 100;
    20. // this->a = 100; //因为const,会报错
    21. // this->b = 200; //因为const,会报错
    22. // this = 0x11; //因为const,会报错
    23. std::cout << "a: " << this->a << "\t";
    24. std::cout << "b: " << this->b << std::endl;
    25. }
    26. protected:
    27. private:
    28. int a;
    29. int b;
    30. };
    31. int main()
    32. {
    33. int *m_space = new int[0];
    34. if (m_space == NULL)
    35. {
    36. return -1;
    37. }
    38. Test t1(1, 2);
    39. t1.printT();
    40. //a: 1 b: 2
    41. t1.OperateVar(100, 200);
    42. //a: 1 b: 2
    43. return 0;
    44. }

    end

  • 相关阅读:
    datepicker设置中文
    Linux上编译sqlite3库出现undefined reference to `sqlite3_column_table_name‘
    Zookeeper快速入门(Zookeeper概述、安装、集群安装、选举机制、命令行操作、节点类型、监听器原理)
    Qt优秀开源项目之九:qTox
    “亚马逊依赖”之下,傲基科技的品牌势能如何提升?
    【数据结构】——顺序表
    万宾科技智能井盖传感器,提升市政井盖健康
    MySQL之导入导出&视图&索引&执行计划
    初识webGL
    CSS常见用法 以及JS基础语法
  • 原文地址:https://blog.csdn.net/hutaotaotao/article/details/138139427