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

news2024/11/15 15:58:05

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.coloradmin.cn/o/2165657.html

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

相关文章

《声入人心》团综重启,芒果能否再造一个群像神话?

随着《声入人心》团综《吾湖音乐局》于9月20日宣布重启&#xff0c;芒果的又一群像综艺“杀”回了市场。 从2018年音综市场冲出的一匹黑马&#xff0c;到2024年“声人”分散在影视综各个领域&#xff0c;这六年间芒果上演了无数次“狼来了”&#xff0c;但这一次团综是真的来了…

实现一个超轻量级实例分割网络的思路

文章目录 前言一、基本思路二、picodet三、yolact三、picodetyolact总结 前言 在某些工业领域&#xff0c;由于成本问题算力有限&#xff0c;只能实时跑一些超轻量级网络&#xff0c;拿目标检测来说&#xff0c;例如yolo-fast&#xff0c;pp-picodet这些。如果要跑实例分割&am…

魅思-视频管理系统 getOrderStatus SQL注入漏洞复现

0x01 产品简介 魅思-视频管理系统是一款集成了视频管理、用户管理、手机端应用封装等功能的综合性视频管理系统。该系统不仅以其强大的视频管理功能、灵活的用户管理机制、便捷的手机端应用封装功能以及高安全性和现代化的界面设计,成为了市场上备受关注的视频管理系统之一。…

分布式数据库——HBase基本操作

启动HBase: 1.启动hadoop,进入hadoop的sbin中 cd /opt/hadoop/sbin/ 2.初始化namenode hdfs namenode -format 3.启动hdfs ./start-all.sh 4.启动hbase cd /opt/hbase/bin ./start-hbase.sh 5.使用jps查看进程 jps 以下图片则是hbase启动成功~ 运行HBase ./hbase sh…

软考中项第3版新教程变化,2张表格看到底

近期正在着手做《信息系统项目管理师一站通关》书友会的见面礼包时&#xff0c;无意中在电脑中翻到不知道什么时候保存的2张表格&#xff0c;出处已经记不得了&#xff0c;这2张表格对软考中项第3版新教程的变化点总结的言简意赅&#xff0c;有价值分享出来给你。 第1张表格如下…

使用C计算数码管段码

前言 平时使用数码管时为了避免使用跳线往往不会按照顺序焊接数码管的段选引脚&#xff0c;为了焊接的方便段选引脚可能会焊接的乱七八糟的&#xff0c;此时标准的段码表就用不了了&#xff0c;需要重新去计算。因为在焊接时为了考虑布线可能每次焊的顺序都会有不同&#xff0c…

使用vite+react+ts+Ant Design开发后台管理项目(三)

前言 本文将引导开发者从零基础开始&#xff0c;运用vite、react、react-router、react-redux、Ant Design、less、tailwindcss、axios等前沿技术栈&#xff0c;构建一个高效、响应式的后台管理系统。通过详细的步骤和实践指导&#xff0c;文章旨在为开发者揭示如何利用这些技术…

使用AI进行需求分析的案例研究

生成式 AI 的潜在应用场景似乎无穷无尽。虽然这令人兴奋&#xff0c;但也可能让人不知所措。因此&#xff0c;团队在使用这项技术时需要有明确的目标&#xff1a;关键是要明确生成式 AI 在团队工作中能产生哪些实质性影响。 在软件工程中&#xff0c;一个引人注目的应用场景是…

QMT如何获取股票基本信息?如上市时间、退市时间、代码、名称、是否是ST等。QMT量化软件支持!

获取股票概况 包含股票的上市时间、退市时间、代码、名称、是否是ST等。 #获取合约基础信息数据 该信息每交易日9点更新 #内置Python 提示 旧版本客户端中&#xff0c;函数名为ContextInfo.get_instrumentdetail 调用方法 内置python ContextInfo.get_instrument_detai…

全连接神经网络

这里写目录标题 全连接神经网络vs前馈神经网络基于全连接神经网络的手写数字识别使用Pytorch实现纯Python实现 全连接神经网络的局限 端到端学习 深度学习有时也称为端到端机器学习&#xff08;end-to-end machine learning&#xff09;。这里所说的端到端是指从一端到另一端的…

西门子因为TC在与PTC及达索的混战中占据优势,西门子与SAP的合作价值几何?(2)

今天这篇文章是通过腾讯会议先录了一个视频然后转录的&#xff0c;看看效果怎么样。 主要是我昨天写了一篇公众号的文章&#xff0c;这篇文章是转自国外的一个记者。写的是他对这个工业软件三巨头西门子、达索和PTC之间的竞争做的一个访谈性质的文章&#xff0c;谈了他的一些看…

1.1.5 计算机网络的性能指标(上)

信道&#xff1a; 表示向某一方向传送信息的通道&#xff08;信道&#xff01;通信线路&#xff09;一条通信线路在逻辑上往往对应一条发送信道和一条接收信道。 速率&#xff1a; 指连接到网络上的节点在信道上传输数据的速率。也称数据率或比特率、数据传输速率。 速率单…

python常见的魔术方法

什么是魔术方法 Python类的内置方法&#xff0c;各自有各自的特殊功能&#xff0c;被称之为魔术方法 常见的魔术方法有以下&#xff1a; __init__:构造方法 __str__:字符串方法 __lt__:小于、大于符号比较 __le__:小于等于、大于等于符合比较 __eq__:等于符合比较__init__ c…

【论文速看】DL最新进展20240925-医学图像分割、目标跟踪、图像超分

目录 【医学图像分割】【目标跟踪】【图像超分】 【医学图像分割】 [2024] UU-Mamba: Uncertainty-aware U-Mamba for Cardiovascular Segmentation 论文链接&#xff1a;https://arxiv.org/pdf/2409.14305 代码链接&#xff1a;https://github.com/tiffany9056/UU-Mamba 在深…

【NLP】循环神经网络--RNN学习.day3

一.初步认识RNN 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一种用于处理序列数据的深度学习模型。与传统的静态神经网络相比&#xff0c;RNN 可以有效处理输入数据的时间序列特性。这使得 RNN 在处理自然语言处理&#xff08;NLP&#xff09;、时间…

【Python报错已解决】TypeError: forward() got an unexpected keyword argument ‘labels‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 专栏介绍 在软件开发和日常使用中&#xff0c;BUG是不可避免的。本专栏致力于为广大开发者和技术爱好者提供一个关于BUG解决的经…

使用API有效率地管理Dynadot域名,注册域名服务器(NS)信息

前言 Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮箱&…

【9.模块化开发和代码重用之——头文件、动静态库】

目录 前言软件工程的基本原则程序的模块化开发和代码重用技术开发自己的头文件定义实现自己的头文件编写实现文件&#xff08;源文件&#xff09;编译代码链接目标文件到可执行文件 实现类似标准库效果的几种方法实际使用的开发方法头文件库 尝试自动链接静态库&#xff08;好像…

替换jar包中class文件

虽然.jar文件是一种Java归档文件&#xff0c;可以使用压缩软件打开&#xff0c;但是并不能通过压缩软件来修改其内容&#xff0c;只能通过jar命令来更新文件。 一、背景 在使用血氧仪SDK时出现了问题&#xff0c;经过分析是在申请权限时版本不兼容导致的闪退异常&#xff0c;…

大数据新视界 --大数据大厂之 Kylin 多维分析引擎实战:构建数据立方体

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…