欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 文化 > C++ 嵌套类 (详解 一站式讲解)

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

2025/4/29 13:00:14 来源:https://blog.csdn.net/m0_74696257/article/details/147597582  浏览:    关键词:C++ 嵌套类 (详解 一站式讲解)

目录

嵌套类

嵌套类的定义

嵌套类结构的访问权限

pimpl模式(了解)


嵌套类

嵌套类的定义

首先介绍两个概念:

  • 类作用域(Class Scope)

类作用域是指在类定义内部的范围。在这个作用域内定义的成员(包括变量、函数、类型别名等)可以被该类的所有成员函数访问。类作用域开始于类定义的左花括号,结束于类定义的右花括号。在类作用域内,成员可以相互访问,无论它们在类定义中的声明顺序如何。

  • 类名作用域(Class Name Scope)

类名作用域指的是可以通过类名访问的作用域。这主要用于访问类的静态成员嵌套类型。类名必须用于访问静态成员或嵌套类型,除非在类的成员函数内部,因为它们不依赖于类的任何特定对象。以静态成员为例:

class MyClass
{
public:void func(){_b = 100;//类的成员函数内访问_b}static int _a;int _b;
};
//静态成员要定义在类外,因为静态成员是一个类所共有的,如果声明在类中,每创建一个类对象就会生成一个静态数据成员
int MyClass::_a = 0;void test0(){//这里静态数据成员为公有所以可以在类外通过类名直接访问MyClass::_a = 200;//类外部访问_a
}

在函数和其他类定义的外部定义的类称为全局类,绝大多数的 C++ 类都是全局类。我们在前面定义的所有类都在全局作用域中,全局类具有全局作用域。

与之对应的,一个类A还可以定义在另一类B的定义中,这就是嵌套类结构。A类被称为B类的内部类,B类被称为A类的外部类

以Point类和Line类为例

class Line
{
public:class Point{public:Point(int x,int y): _ix(x), _iy(y){}private:int _ix;int _iy;};
public:Line(int x1, int y1, int x2, int y2): _pt1(x1,y1), _pt2(x2,y2){}
private: Point _pt1;Point _pt2;
};

Point类是定义在Line类中的内部类,无法直接创建Point对象,需要在Line类名作用域中才能创建,因为point类在line类中,只能先找到line类在访问point类

Point pt(1,2);//error
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的友元函数

下面为测试代码,可自行测试

#include <iostream>
using namespace std;
class Line
{
public:class Point {public:Point(int x, int y): _ix(x), _iy(y){}friend ostream& operator<<(ostream& os, const Line::Point& rhs);private:int _ix;int _iy;};
public:Line(int x1, int y1, int x2, int y2): _pt1(x1, y1), _pt2(x2, y2){}~Line() {//cout << "~Line()" << endl;}friend ostream& operator<<(ostream& os, const Line& rhs);friend ostream& operator<<(ostream& os, const Point& rhs);private:Point _pt1;Point _pt2;
};//这里会访问line的私有成员point所以在line类中声明为友元,因为又要访问point对象的数据成员,
//所以又要将这个函数声明为内部类的友元
ostream& operator<<(ostream& os, const Line::Point& rhs)
{os << "(" << rhs._ix << "," << rhs._iy << ")";return os;
}//因为是输出运算符重载,不会对操作数进行修改,倾向于声明为有友元类
ostream& operator<<(ostream & os, const Line & rhs)
{os << rhs._pt1 << "------->" << rhs._pt2;return os;
}
void test()
{Line ll(1, 2, 3, 4);cout << ll << endl;
}
int main()
{test();return 0;
}

嵌套类结构的访问权限

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

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

image-20240304153639911

内部类相当于是定义在外部类中的外部类的友元类

类A定义在类B中,那么类A访问类B的成员时,就相当于默认的是类B的友元类。

下面为测试代码,可以自行测试

#include <iostream>
using namespace std;
class Line
{
public:class Point {public:Point(int x, int y): _ix(x), _iy(y){}friend ostream& operator<<(ostream& os, const Line::Point& rhs);void print(){cout << "print" << endl;}friend class Line;void getline(const Line& rhs){//在内部类中不需要友元声明,可以直接通过line对象直接访问成员rhs._pt1;rhs._pt2;//通过类名作用域直接访问line的私有静态成员Line::_pt3;//直接用成员名访问line的私有静态数据成员_pt3;}private:int _ix;int _iy;static int _iz;};
public:Line(int x1, int y1, int x2, int y2): _pt1(x1, y1), _pt2(x2, y2){}~Line() {//cout << "~Line()" << endl;}friend ostream& operator<<(ostream& os, const Line& rhs);friend ostream& operator<<(ostream& os, const Point& rhs);void getpoint(){//在外部类中通过内部类访问内部类的公有成员_pt1.print();//ok//外部类属于内部类的类定义之外,这里是在内部类的私有数据成员//在外部类中通过内部类对象访问内部类的私有成员//_pt1._ix;//需要声明友元Point::_iz;//因为为私有,所以需要友元声明//_ix;不可能实现//_iy;同上}
private:Point _pt1;Point _pt2;static double _pt3;
};
//要在外部类的外面对内部类的静态成员进行定义
int Line::Point::_iz = 10;
double Line::_pt3 = 100;
//这里会访问line的私有成员point所以在line类中声明为友元,因为又要访问point对象的数据成员,
//所以又要将这个函数声明为内部类的友元
ostream& operator<<(ostream& os, const Line::Point& rhs)
{os << "(" << rhs._ix << "," << rhs._iy << ")";return os;
}//因为是输出运算符重载,不会对操作数进行修改,倾向于声明为有友元类
ostream& operator<<(ostream & os, const Line & rhs)
{os << rhs._pt1 << "------->" << rhs._pt2;return os;
}
void test()
{Line ll(1, 2, 3, 4);cout << ll << endl;
}
int main()
{test();return 0;
}

pimpl模式(了解)

实际项目的需求:希望Line的实现全部隐藏,在源文件中实现,再将其打包成库文件,交给第三方使用。

(1)头文件只给出接口:

//Line.hpp
class Line{
public:Line(int x1, int y1, int x2, int y2);~Line();void printLine() const;//打印Line对象的信息
private:class LineImpl;//类的前向声明LineImpl * _pimpl;
};

(2)在实现文件中进行具体实现,使用嵌套类的结构(LineImpl是Line的内部类,Point是LineImpl的内部类),Line类对外公布的接口都是使用LineImpl进行具体实现的

在测试文件中创建Line对象(最外层),使用Line对外提供的接口,但是不知道具体的实现

//LineImpl.cc
class Line::LineImpl
{class Point{public:Point(int x,int y): _ix(x), _iy(y){}//...private:int _ix;int _iy;};//...
};//Line.cc
void test0(){Line line(10,20,30,40);line.printLine();
}

(3)打包库文件,将库文件和头文件交给第三方

sudo apt install build-essential
g++ -c LineImpl.cc
ar rcs libLine.a LineImpl.o生成libLine.a库文件
编译:g++ Line.cc(测试文件) -L(加上库文件地址) -lLine(就是库文件名中的lib缩写为l,不带后缀)
此时的编译指令为 g++ Line.cc -L. -lLine

内存结构

pimpl模式是一种减少代码依赖和编译时间的C++编程技巧,其基本思想是将一个外部可见类的实现细节(一般是通过私有的非虚成员)放在一个单独的实现类中,在可见类中通过一个私有指针来间接访问该类型。

好处:

  1. 实现信息隐藏;

  2. 只要头文件中的接口不变,实现文件可以随意修改,修改完毕只需要将新生成的库文件交给第三方即可;

  3. 可以实现库的平滑升级。

下面为测试代码,可自行测试

//LineImpl.cc#include <iostream>
#include "Line.hpp"
using namespace std;
// 这是一个实现文件,不用包含测试,只需要实现头文件中的函数等内容
//先对成员类型进行实现
class Line::LineImpl
{
public:class Point{public:Point(int x,int y): _ix(x), _iy(y){}~Point(){cout << "~Point()" << endl;}void print(){cout << "("<<_ix << "," << _iy << ")";}private:int _ix;int _iy;};
public:LineImpl(int x1, int y1, int x2, int y2): _pt1(x1,y1), _pt2(x2,y2){cout << "LineImpl(int *4)" << endl;}~LineImpl(){cout << "~LineImpl()" << endl;}void printline(){_pt1.print();cout << "-----> ";_pt2.print();cout << endl;}
private: Point _pt1;Point _pt2;
};Line::Line(int x1, int y1, int x2, int y2):_pimpl(new LineImpl(x1,y1, x2,y2))
{cout << "Line(int * 4)" << endl;
}
Line :: ~Line()
{cout << "~Line()" << endl;if(_pimpl){delete _pimpl;_pimpl = nullptr;}
}
void Line::printLine() const{_pimpl->printline();
}// test.cc#include "Line.hpp"
#include <iostream>
using std::cout;
using std::endl;void test()
{Line ll(1,2,3,4);ll.printLine();
}int main()
{test();return 0;
}//Line.hpp#ifndef _Line_HPP_
#define _Line_HPP_class Line {
public:Line(int x1, int y1, int x2, int y2);~Line();//提供给客户使用的功能void printLine() const;//打印Line对象的信息
private:class LineImpl;//类的前向声明LineImpl* _pimpl;
};#endif

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词