C++学习笔记----8、掌握类与对象(一)---- 对象中的动态内存分配(1)

1、FRIENDS

        c++允许类声明为其它类,其它类的成员函数,或者非成员函数为friend。可以访问protected与private数据成员与成员函数。例如,假设你有两个类Foo与Bar。你可以指定Bar类是Foo类的一个friend:

class Foo
{friend class Bar;// ...
};

        这样Bar类的所有成员函数可以访问Foo类的private与protected数据成员与成员函数。

        如果你只是想让Bar类的一个特定成员函数做friend,也可以这样做。假设Bar类有一个成员函数processFoo(const Foo&)。下面的语法用于让这个成员函数成为Foo的一个friend:

class Foo
{friend void Bar::processFoo(const Foo&);// ...
};

        独立的函数也可以成为类的friend。举例来说,你可能想要写一个函数来打印Foo对象的所有数据到控制台。可能想要让该函数在Foo类之外,因为打印不是Foo的核心功能,但是该函数需要访问对象的内部数据成员以便进行打印。下面是Foo类的定义,以printFoo()作为friend:

class Foo
{friend void printFoo(const Foo&);// ...
};

        在类中的friend声明作为函数的原型。没有必要在其它地方再写原型了(写了也没什么坏处)。

        下面是函数的定义:

void printFoo(const Foo& foo)
{// Print all data of foo to the console, including// private and protected data members.
}

        在类定义之外写这样的函数与其它任何函数一样,除了可以直接访问Foo的private与protected成员。在函数定义时不必重复friend关键字。

        注意类需要知道其它哪些类,成员函数,或者函数想要成为它的friend;一个类,成员函数,或者函数不能声明自己为一些其它类的friend来获得那个类的非public成员的访问。

        friend类与函数容易被滥用;它们允许你破坏通过暴露类内部给其他类或函数来进行包装的原则。这样的话,只能在有限的情况下使用它们。我们会在博文中进行使用场景的展示。

2、对象中的动态内存分配

        有时候你不知道在程序真正运行之前需要多大内存。解决方案是在程序执行时动态分配需要的内存。类也不例外。有时候你不知道在写类时一个对象需要多大内存。在这种情况下,对象应该动态分配内存。给对象动态分配内存带来了一些挑战,包括释放内存,处理对象拷贝,以及处理对象赋值。

2.1、Spreadsheet类

        我们前面介绍过SpreadsheetCell类。现在我们继续写Spreadsheet类。与SpreadsheetCell类一样,Spreadsheet类也在我们的文章中不断演化。这样的话,多种尝试并不总是去演示类书写的每个方面的最好的方式。

        我们先开始,Spreadsheet只是一个简单的SpreadsheetCell的二维数组,在Spreadsheet中带有成员函数来设置与访问其特定位置的cell。虽然大部分spreadsheet应用在一个方向上使用字母,在另一个方向上使用数字来指向cell,我们的Spreadsheet在两个方向上都使用数字。

        Spreadsheet.cppm模块接口文件的第一行定义了模块的名字:

export module spreadsheet;

        Spreadsheet类需要访问SpreadsheetCell类,所以它需要import spreadsheet_cell模块。另外,为了使SpreadsheetCell类对spreadsheet模块中的用户可见,spreadsheet_cell模块需要用下面的看起来很可笑的语法来进行导入导出:

export import spreadsheet_cell;

        Spreadsheet类使用std::size_t类型,它定义在C头文件中<cstddef>。可以用下面的导入来获得访问权限:

import std;

        最终,下面是Spreadsheet类的定义的第一次尝试:

export class Spreadsheet
{
public:Spreadsheet(std::size_t width, std::size_t height);void setCellAt(std::size_t x, std::size_t y, const SpreadsheetCell& cell);SpreadsheetCell& getCellAt(std::size_t x, std::size_t y);private:bool inRange(std::size_t value, std::size_t upper) const;std::size_t m_width{ 0 };std::size_t m_height{ 0 };SpreadsheetCell** m_cells{ nullptr };
};

        注意:Spreadsheet类使用指向m_cells数组的正常指针。这样做的目的是为了展示结果以及解释怎么处理资源,比如类中的动态内存。在生产环境的代码中,应该使用标准c++容器,像std::vector,会大幅简化Spreadsheet的实现,但那样的话,你就无法学习如何使用原始指针正确处理动态内存。在现代c++中,永远不要使用带有属主语法的原始指针,但是对于既有代码你可能会碰到,这种情况下需要知道如何处理。

        注意Spreadsheet类并不包含标准的SpreadsheetCell二维数组。实际上,它包含了一个SpreadsheetCell**的数据成员,是一个指向代表了一个数组的数组的指针。这是因为每一个Spreadsheet对象可能有不同的维度,所以类的构造函数需要动态分配二维数组,基于客户指定的高度与宽度。

        为了动态分配一个二维数组,需要写下面的代码。记住在c++中,不像Java,不能只是简单地写new SpreadsheetCell[m_width][m_height]。

Spreadsheet::Spreadsheet(size_t width, size_t height): m_width { width }, m_height { height }
{m_cells = new SpreadsheetCell*[m_width];for (size_t i{ 0 }; i < m_width; ++i) {m_cells[i] = new SpreadsheetCell[m_height];}
}

        下图展示了叫做s1的Spreadsheet的结果内存结构,在栈上,宽度为4,高度为3。

        inRange()的实现,设置与访问成员函数就很直接了:

bool Spreadsheet::inRange(size_t value, size_t upper) const
{return value < upper;
}void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{if (!inRange(x, m_width)) {throw out_of_range { format("x ({}) must be less than width ({}).", x, m_width) };}if (!inRange(y, m_height)) {throw out_of_range { format("y ({}) must be less than height ({}).", y, m_height) };}m_cells[x][y] = cell;
}SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y)
{if (!inRange(x, m_width)) {throw out_of_range { format("x ({}) must be less than width ({}).", x, m_width) };}if (!inRange(y, m_height)) {throw out_of_range { format("y ({}) must be less than height ({}).", y, m_height) };}return m_cells[x][y];
}

        setCellAt()与getCellAt()两者都使用了类的辅助函数inRange()来检查x与y,它们代表了spreadsheet中的有效坐标。尝试访问越界索引的数组元素会导致程序运行不正确。本例使用了例外,以后会详细介绍。

        如果你仔细看setCellAt()与getCellAt()的实现,可以看到有很清晰的代码重复。我们以前说过要尽量避免代码重复。所以,让我们遵从指导,不使用inRange()的辅助函数,定义一个verifyCoordinate()的成员函数:

void verifyCoordinate(std::size_t x, std::size_t y) const;

        其实现检查给定的坐标,如果坐标无效的话抛出例外:

void Spreadsheet::verifyCoordinate(size_t x, size_t y) const
{if (x >= m_width) {throw out_of_range { format("x ({}) must be less than width ({}).", x, m_width) };}if (y >= m_height) {throw out_of_range { format("y ({}) must be less than height ({}).", y, m_height) };}
}

        这样的话,setCellAt()与getCellAt()就可以简化如下:

void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{verifyCoordinate(x, y);m_cells[x][y] = cell;
}SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y)
{verifyCoordinate(x, y);return m_cells[x][y];
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/149609.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

C++之哈希 --- 哈希的应用(位图布隆过滤器)

一、位图 1.1 位图的基本概念 在如今网络交通高度发达的时代&#xff0c;网购已经成为我们日常生活中的一部分。没当双11到来&#xff0c;各大平台都会迎来一次网购的高潮。这就会让服务器短时间内获得高达几十亿上百亿的数据&#xff0c;那我们该如何去处理这海量的数据呢&am…

FortiGate 防火墙 DNS 地址转换(DNS Translation)

简介 本例介绍 FortiGate 防火墙 DNS 地址转换&#xff08;DNS Translation&#xff09;配置方法。 一、 网络结构 网络结构如下图&#xff0c;PC1 连接在 FG60B 的 Internal 接口&#xff0c;FG60B 的 Wan1 接口连接 FG80CM 的 DMZ 接口&#xff0c;Wan1 接口开启 DNS 服务…

开发环境搭建之windows和ubuntu系统互传文件

ubuntu和Windows主机之间的文件传输有很多种&#xff0c;安装VMware Tools后&#xff0c;可以设置虚拟机共享文件夹&#xff0c;将Windows主机的文件目录挂载到ubuntu中&#xff0c;实现文件共享。 设置方法如下&#xff0c;点击菜单栏的“虚拟机”&#xff0c;选择“设置”。…

【LLM大模型】如何让大模型更好地进行场景落地?

自ChatGPT模型问世后&#xff0c;在全球范围内掀起了AI新浪潮。 有很多企业和高校也随之开源了一些效果优异的大模型&#xff0c;例如&#xff1a;Qwen系列模型、MiniCPM序列模型、Yi系列模型、ChatGLM系列模型、Llama系列模型、Baichuan系列模型、Deepseek系列模型、Moss模型…

[OPEN SQL] SELECT语句

本次操作使用的数据库表为SCUSTOM&#xff0c;其字段内容如下所示 航班用户(SCUSTOM) 1.SELECT语句 SELECT语句从数据库表中读取必要的数据 1.1 读取一行数据 语法格式 SELECT SINGLE <cols>... WHERE cols&#xff1a;数据库表的字段 从数据库表中读取一条数据可使…

用CPU训练机器学习模型

人工智能最近的成功通常归功于 GPU 的出现和发展。GPU 的架构通常包括数千个多处理器、高速内存、专用张量核心等&#xff0c;特别适合满足人工智能/机器学习工作负载的密集需求。 不幸的是&#xff0c;人工智能开发的快速增长导致对 GPU 的需求激增&#xff0c;使得 GPU 难以…

Java面试篇基础部分- 锁详解

可重入锁 可重入锁也叫作递归锁,是指在同一个线程中,在外层函数获取到该锁之后,内存的递归函数还可以获取到该锁。在Java语言环境下,ReentrantLock和Synchroinzed都是可重入锁的代表。 公平锁与非公平锁 公平锁(Fair Lock)是指在分配锁之前检查是否有线程在排队等待获取…

学习MRI处理过程中搜到的宝藏网站

今天浏览网页查到了一些宝藏网站&#xff0c;正好记录一下&#xff0c;后面搜到好东东再接着填充&#xff0c;方便查阅~ &#xff08;1&#xff09;牛人网站 这个网站是在搜集seed关键词时发现的&#xff0c;用pdf文档记录&#xff0c;可下载查阅&#xff0c;条理清晰&#xf…

ios swift5 UITextView占位字符,记录限制字数

文章目录 截图代码&#xff1a;具体使用代码&#xff1a;CustomTextView 截图 代码&#xff1a;具体使用 scrollView.addSubview(contentTextView)contentTextView.placeholderLabel.text LocalizableManager.localValue("write_comment")contentTextView.maxCharac…

ActiveMQ 的传输协议机制

ActiveMQ 通过网络连接器这种连接机制来实现客户端与服务端之间的通信&#xff0c;ActiveMQ支持的传输协议在activeMQ 安装目录的 conf/activemq.xml中的<transportConnectors>标签之内。 ActiveMQ 支持的 client 端和 broker 端的通讯协议有&#xff1a;TCP、NIO、UDP、…

芝法酱学习笔记(0.3)——SpringBoot下的增删改查

零、前言 书接上回&#xff0c;我们搭建了windows下的开发环境&#xff0c;并给出了一个hello world级别的多模块SpringBoot项目。 毕竟java后端开发&#xff0c;离不开数据库的操作&#xff0c;为方便后面内容的讲解&#xff0c;这里再做一期铺垫&#xff0c;core模块下新增一…

洛汗2搬砖攻略:VMOS云手机一键搬砖辅助教程!

在《洛汗2》的世界中&#xff0c;玩家往往需要长时间刷怪、任务和升级&#xff0c;手动操作往往会耗费大量时间和精力。这时候&#xff0c;使用VMOS云手机来辅助游戏&#xff0c;将是一个极佳的选择。VMOS云手机专为《洛汗2》提供了专属定制版云手机&#xff0c;内置游戏安装包…

2.Spring-容器-注入

注册&#xff1a;将组件放入容器中&#xff1b; 注入&#xff1a;让容器按需进行操作&#xff1b; 一、Autowired&#xff1a;自动注入组件 原理&#xff1a;Spring调用容器的getBean 二、Qualifier 精确指定 精确指定&#xff1a;如果容器中组件存在多个&#xff0c;则使用…

【Linux】ubuntu 16.04 搭建jdk 11 环境(亲测可用)

目录 0.环境 1.题外话 2.详细 0.环境 windows11 主机 Virtual Box 7.0 ubuntu 16.04系统 想搭建个 jdk11的环境&#xff0c;用于项目 1.题外话 因为虚拟机与主机传输文件不方便&#xff0c;所以可以尝试用共享文件夹的方式传输&#xff0c;亲测可用&#xff0c;参考以下博…

LeetCode题练习与总结:二叉树的最近公共祖先--236

一、题目描述 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为&#xff1a;“对于有根树 T 的两个节点 p、q&#xff0c;最近公共祖先表示为一个节点 x&#xff0c;满足 x 是 p、q 的祖先且 x 的深度尽可能大&#xff08;一个节点也…

windows 安装配置nginx

进入到对应的nginx的配置文件里 80的这个server块里 添加 location / { return 301 https://$host$request_uri; # 301 永久重定向到 HTTPS } (如果是http重定向https&#xff0c;其他的可以删了 只保留截图中的内容) https的server块里面添加 证书的…

SLM7888兼容FAN7888—— 低压三相半桥驱动的理想之选

SLM7888系列型号&#xff1a; SLM7888CH&#xff1a;SOP20W SLM7888MD&#xff1a;TSSOP20 SLM7888是一款高压、高速的功率MOSFET和IGBT驱动器&#xff0c;它提供三个独立的高边、低边输出驱动信号便于用于三相电路。采用专有的高压集成电路和锁存免疫CMOS技术&…

二分查找算法(5) _山脉数组的峰顶索引

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 二分查找算法(5) _山脉数组的峰顶索引 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c;…

< 微积分Calculus >

微积分 微分是把整体分拆为小部分来求它怎样改变 积分是把小部分连接在一起来求整体有多大&#xff0c;可以用来求面积、体积、中点和很多其他有用的东西。 lim极限 函数f(x) -> Q(x) y&#xff1a;x变量&#xff0c;f函数&#xff0c;Q(x)函数体&#xff08;多项式&am…

Centos下安装Maven(无坑版)

Linux 安装 Maven Maven 压缩包下载与解压 华为云下载源&#xff0c;自行选择版本 下面的示例使用的是 3.8.1 版本 wget https://repo.huaweicloud.com/apache/maven/maven-3/3.8.1/binaries/apache-maven-3.8.1-bin.tar.gz解压 tar -zxvf apache-maven-3.8.1-bin.tar.gz移…