C++ 嵌套类 (详解 一站式教学)

[复制链接]
发表于 2025-9-22 04:34:24 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
目次

嵌套类
嵌套类的界说
嵌套类结构的访问权限
pimpl模式(了解)


嵌套类

嵌套类的界说

起首介绍两个概念:


  • 类作用域(Class Scope)
类作用域是指在类界说内部的范围。在这个作用域内界说的成员(包括变量、函数、范例别名等)可以被该类的全部成员函数访问。类作用域开始于类界说的左花括号,结束于类界说的右花括号。在类作用域内,成员可以相互访问,无论它们在类界说中的声明次序怎样。


  • 类名作用域(Class Name Scope)
类名作用域指的是可以通过类名访问的作用域。这紧张用于访问类的静态成员、嵌套范例。类名必须用于访问静态成员或嵌套范例,除非在类的成员函数内部,由于它们不依赖于类的任何特定对象。以静态成员为例:
  1. class MyClass
  2. {
  3. public:
  4.     void func(){
  5.         _b = 100;//类的成员函数内访问_b
  6.     }
  7.     static int _a;
  8.     int _b;
  9. };
  10. //静态成员要定义在类外,因为静态成员是一个类所共有的,如果声明在类中,每创建一个类对象就会生成一个静态数据成员
  11. int MyClass::_a = 0;
  12. void test0(){
  13.     //这里静态数据成员为公有所以可以在类外通过类名直接访问
  14.     MyClass::_a = 200;//类外部访问_a
  15. }
复制代码

在函数和其他类界说的外部界说的类称为全局类,绝大多数的 C++ 类都是全局类。我们在前面界说的全部类都在全局作用域中,全局类具有全局作用域。
与之对应的,一个类A还可以界说在另一类B的界说中,这就是嵌套类结构。A类被称为B类的内部类,B类被称为A类的外部类。
以Point类和Line类为例
  1. class Line
  2. {
  3. public:
  4.     class Point{
  5.     public:
  6.         Point(int x,int y)
  7.         : _ix(x)
  8.         , _iy(y)
  9.         {}
  10.     private:
  11.         int _ix;
  12.         int _iy;
  13.     };
  14. public:
  15.     Line(int x1, int y1, int x2, int y2)
  16.     : _pt1(x1,y1)
  17.     , _pt2(x2,y2)
  18.     {}
  19. private:
  20.     Point _pt1;
  21.     Point _pt2;
  22. };
复制代码

Point类是界说在Line类中的内部类,无法直接创建Point对象,必要在Line类名作用域中才气创建,由于point类在line类中,只能先找到line类在访问point类
  1. Point pt(1,2);//error
  2. Line::Point pt2(3,4);//ok
复制代码
  Point类是Line类的内部类,并不代表Point类的数据成员会占据Line类对象的内存空间,在存储关系上并不是嵌套的结构。
  只有当Line类有Point类范例的对象成员时,Line类对象的内存结构中才会包含Point类对象(成员子对象)。
  
  (1)如果Line类中没有Point类的对象成员,sizeof(Line) = 8;
  (2)如果Line类中有两个Point类的对象成员,sizeof(Line) = 24;
  
  

   思索,如果想要利用输出流运算符输出Line对象,应该怎么实现?(紧张)
  
  最直观的实现方式是界说一个运算符重载函数,但在函数体中必要让输出流运算符处置处罚Point范例对象,以是还必要为Point类准备一个输出流运算符重载函数。
  
  
  —— 如果Point界说在Line的私有地区
  那么还必要将operator<<函数声明为Line的友元函数
  
下面为测试代码,可自行测试
  1. #include <iostream>
  2. using namespace std;
  3. class Line
  4. {
  5. public:
  6.     class Point {
  7.     public:
  8.         Point(int x, int y)
  9.             : _ix(x)
  10.             , _iy(y)
  11.         {}
  12.         friend ostream& operator<<(ostream& os, const Line::Point& rhs);
  13.     private:
  14.         int _ix;
  15.         int _iy;
  16.     };
  17. public:
  18.     Line(int x1, int y1, int x2, int y2)
  19.         : _pt1(x1, y1)
  20.         , _pt2(x2, y2)
  21.     {}
  22.     ~Line() {
  23.         //cout << "~Line()" << endl;
  24.     }
  25.     friend ostream& operator<<(ostream& os, const Line& rhs);
  26.     friend ostream& operator<<(ostream& os, const Point& rhs);
  27.    
  28. private:
  29.     Point _pt1;
  30.     Point _pt2;
  31. };
  32. //这里会访问line的私有成员point所以在line类中声明为友元,因为又要访问point对象的数据成员,
  33. //所以又要将这个函数声明为内部类的友元
  34. ostream& operator<<(ostream& os, const Line::Point& rhs)
  35. {
  36.     os << "(" << rhs._ix << "," << rhs._iy << ")";
  37.     return os;
  38. }
  39. //因为是输出运算符重载,不会对操作数进行修改,倾向于声明为有友元类
  40. ostream& operator<<(ostream & os, const Line & rhs)
  41. {
  42.     os << rhs._pt1 << "------->" << rhs._pt2;
  43.     return os;
  44. }
  45. void test()
  46. {
  47.     Line ll(1, 2, 3, 4);
  48.     cout << ll << endl;
  49. }
  50. int main()
  51. {
  52.     test();
  53.     return 0;
  54. }
复制代码

嵌套类结构的访问权限

外部类对内部类的成员举行访问
内部类对外部类的成员举行访问


内部类相称于是界说在外部类中的外部类的友元类
类A界说在类B中,那么类A访问类B的成员时,就相称于默认的是类B的友元类。

下面为测试代码,可以自行测试
  1. #include <iostream>
  2. using namespace std;
  3. class Line
  4. {
  5. public:
  6.     class Point {
  7.     public:
  8.         Point(int x, int y)
  9.             : _ix(x)
  10.             , _iy(y)
  11.         {}
  12.         friend ostream& operator<<(ostream& os, const Line::Point& rhs);
  13.         void print()
  14.         {
  15.             cout << "print" << endl;
  16.         }
  17.         friend class Line;
  18.         void getline(const Line& rhs)
  19.         {
  20.             //在内部类中不需要友元声明,可以直接通过line对象直接访问成员
  21.             rhs._pt1;
  22.             rhs._pt2;
  23.             //通过类名作用域直接访问line的私有静态成员
  24.             Line::_pt3;
  25.             //直接用成员名访问line的私有静态数据成员
  26.             _pt3;
  27.         }
  28.     private:
  29.         int _ix;
  30.         int _iy;
  31.         static int _iz;
  32.     };
  33. public:
  34.     Line(int x1, int y1, int x2, int y2)
  35.         : _pt1(x1, y1)
  36.         , _pt2(x2, y2)
  37.     {}
  38.     ~Line() {
  39.         //cout << "~Line()" << endl;
  40.     }
  41.     friend ostream& operator<<(ostream& os, const Line& rhs);
  42.     friend ostream& operator<<(ostream& os, const Point& rhs);
  43.     void getpoint()
  44.     {
  45.         //在外部类中通过内部类访问内部类的公有成员
  46.         _pt1.print();//ok
  47.         //外部类属于内部类的类定义之外,这里是在内部类的私有数据成员
  48.         //在外部类中通过内部类对象访问内部类的私有成员
  49.         //_pt1._ix;//需要声明友元
  50.         Point::_iz;//因为为私有,所以需要友元声明
  51.         
  52.         //_ix;不可能实现
  53.         //_iy;同上
  54.     }
  55. private:
  56.     Point _pt1;
  57.     Point _pt2;
  58.     static double _pt3;
  59. };
  60. //要在外部类的外面对内部类的静态成员进行定义
  61. int Line::Point::_iz = 10;
  62. double Line::_pt3 = 100;
  63. //这里会访问line的私有成员point所以在line类中声明为友元,因为又要访问point对象的数据成员,
  64. //所以又要将这个函数声明为内部类的友元
  65. ostream& operator<<(ostream& os, const Line::Point& rhs)
  66. {
  67.     os << "(" << rhs._ix << "," << rhs._iy << ")";
  68.     return os;
  69. }
  70. //因为是输出运算符重载,不会对操作数进行修改,倾向于声明为有友元类
  71. ostream& operator<<(ostream & os, const Line & rhs)
  72. {
  73.     os << rhs._pt1 << "------->" << rhs._pt2;
  74.     return os;
  75. }
  76. void test()
  77. {
  78.     Line ll(1, 2, 3, 4);
  79.     cout << ll << endl;
  80. }
  81. int main()
  82. {
  83.     test();
  84.     return 0;
  85. }
复制代码
pimpl模式(了解)

现实项目标需求:盼望Line的实现全部隐藏,在源文件中实现,再将其打包成库文件,交给第三方利用。
(1)头文件只给出接口:
  1. //Line.hpp
  2. class Line{
  3. public:
  4.     Line(int x1, int y1, int x2, int y2);
  5.     ~Line();
  6.     void printLine() const;//打印Line对象的信息
  7. private:
  8.     class LineImpl;//类的前向声明
  9.     LineImpl * _pimpl;
  10. };
复制代码

(2)在实现文件中举行详细实现,利用嵌套类的结构(LineImpl是Line的内部类,Point是LineImpl的内部类),Line类对外公布的接口都是利用LineImpl举行详细实现的
在测试文件中创建Line对象(最外层),利用Line对外提供的接口,但是不知道详细的实现
  1. //LineImpl.cc
  2. class Line::LineImpl
  3. {
  4.     class Point{
  5.     public:
  6.         Point(int x,int y)
  7.             : _ix(x)
  8.               , _iy(y)
  9.         {}
  10.                 //...
  11.     private:
  12.         int _ix;
  13.         int _iy;
  14.     };
  15.     //...
  16. };
  17. //Line.cc
  18. void test0(){
  19.     Line line(10,20,30,40);
  20.     line.printLine();
  21. }
复制代码

(3)打包库文件,将库文件和头文件交给第三方
  1. sudo apt install build-essential
  2. g++ -c LineImpl.cc
  3. ar rcs libLine.a LineImpl.o
  4. 生成libLine.a库文件
  5. 编译:g++ Line.cc(测试文件) -L(加上库文件地址) -lLine(就是库文件名中的lib缩写为l,不带后缀)
  6. 此时的编译指令为 g++ Line.cc -L. -lLine
复制代码

内存结构

pimpl模式是一种镌汰代码依赖和编译时间的C++编程本事,其根本头脑是将一个外部可见类的实现细节(一样平常是通过私有的非虚成员)放在一个单独的实现类中,在可见类中通过一个私有指针来间接访问该范例。
长处:

  • 实现信息隐藏;
  • 只要头文件中的接口稳定,实现文件可以随意修改,修改完毕只必要将新天生的库文件交给第三方即可;
  • 可以实现库的平滑升级。
下面为测试代码,可自行测试
  1. //LineImpl.cc
  2. #include <iostream>
  3. #include "Line.hpp"
  4. using namespace std;
  5. // 这是一个实现文件,不用包含测试,只需要实现头文件中的函数等内容
  6. //先对成员类型进行实现
  7. class Line::LineImpl
  8. {
  9. public:
  10.     class Point{
  11.     public:
  12.         Point(int x,int y)
  13.         : _ix(x)
  14.         , _iy(y)
  15.         {}
  16.         ~Point()
  17.         {
  18.             cout << "~Point()" << endl;
  19.         }
  20.         void print()
  21.         {
  22.             cout << "("<<_ix << ","
  23.                 << _iy << ")";
  24.         }
  25.     private:
  26.         int _ix;
  27.         int _iy;
  28.     };
  29. public:
  30.     LineImpl(int x1, int y1, int x2, int y2)
  31.     : _pt1(x1,y1)
  32.     , _pt2(x2,y2)
  33.     {
  34.         cout << "LineImpl(int *4)" << endl;
  35.     }
  36.     ~LineImpl()
  37.     {
  38.         cout << "~LineImpl()" << endl;
  39.     }
  40.     void printline()
  41.     {
  42.         _pt1.print();
  43.         cout << "-----> ";
  44.         _pt2.print();
  45.         cout << endl;
  46.     }
  47. private:
  48.     Point _pt1;
  49.     Point _pt2;
  50. };
  51. Line::Line(int x1, int y1, int x2, int y2)
  52.     :_pimpl(new LineImpl(x1,y1, x2,y2))
  53. {
  54.     cout << "Line(int * 4)" << endl;
  55. }
  56. Line :: ~Line()
  57. {
  58.     cout << "~Line()" << endl;
  59.     if(_pimpl)
  60.     {
  61.         delete _pimpl;
  62.         _pimpl = nullptr;
  63.     }
  64. }
  65. void Line::printLine() const{
  66.     _pimpl->printline();
  67. }
  68. // test.cc
  69. #include "Line.hpp"
  70. #include <iostream>
  71. using std::cout;
  72. using std::endl;
  73. void test()
  74. {
  75.     Line ll(1,2,3,4);
  76.     ll.printLine();
  77. }
  78. int main()
  79. {
  80.     test();
  81.     return 0;
  82. }
  83. //Line.hpp
  84. #ifndef _Line_HPP_
  85. #define _Line_HPP_
  86. class Line {
  87. public:
  88.     Line(int x1, int y1, int x2, int y2);
  89.     ~Line();
  90.     //提供给客户使用的功能
  91.     void printLine() const;//打印Line对象的信息
  92. private:
  93.     class LineImpl;//类的前向声明
  94.     LineImpl* _pimpl;
  95. };
  96. #endif
复制代码

回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表