Effective C++ 2e Item29

工业模型导入Unity实战:从ProE/SolidWorks到实时渲染的完整流程 在工业仿真、数字孪生和VR/AR应用开发中,将高精度工程模型导入实时渲染引擎是一个常见的技术挑战。其核心原理在于参数化工程软件与实时渲染引擎的数据结构差异:前者基于精确的B-rep边界表示和特征树,后者依赖轻量化的三角网格与材质贴图。这一转换的技术价值在于实现工程数据的可视化与交互化,为产品展示、虚拟培训和工厂仿真等应用场景提供基础。通过数据翻译与优化流程,特别是利用3ds Max或Blender作为中转站进行网格优化与UV展开,能够有效解决模型破面、材质丢失和性能问题。本文聚焦于ProE(Creo)和So 阅读详情

类和函数: 实现

C++是一种高度类型化的语言,所以,给出合适的类和模板的定义以及合适的函数声明是整个设计工作中最大的一部分。按理说,只要这部分做好了,类、模板以及函数的实现就不容易出问题。但是,往往人们还是会犯错。

犯错的原因有的是不小心违反了抽象的原则:让实现细节可以提取类和函数内部的数据。有的错误在于不清楚对象生命周期的长短。还有的错误起源于不合理的前期优化工作,特别是滥用inline关键字。最后一种情况是,有些实现策略会导致源文件间的相互联结问题,它可能在小规模范围内很合适,但在重建大系统时会带来难以接受的成本。

所有这些问题,以及与之类似的问题,都可以避免,只要你清楚该注意哪些方面。以下的条款就指明了应该特别注意的几种情况。


条款29: 避免返回内部数据的句柄

请看面向对象世界里发生的一幕:

对象A:亲爱的,永远别变心!
对象B:别担心,亲爱的,我是const。

然而,和现实生活中一样,A会怀疑,"能相信B吗?" 同样地,和现实生活中一样,答案取决于B的本性:其成员函数的组成结构。

假设B是一个const String对象:

class String {
public:
  String(const char *value);        // 具体实现参见条款11
  ~String();                        // 构造函数的注解参见条款M5

  operator char *() const;          // 转换String -> char*;
                                    // 参见条款M5
  ...

private:
  char *data;
};

const String B("Hello World");      // B是一个const对象

既然B为const,最好的情况当然就是无论现在还是以后,B的值总是"Hello World"。这就寄希望于别的程序员能以合理的方式使用B了。特别是,千万别有什么人象下面这样残忍地将B强制转换掉const(参见条款21):

String& alsoB =              // 使得alsoB成为B的另一个名字,
  const_cast<String&>(B);    // 但不具有const属性

然而,即使没有人做这种残忍的事,就能保证B永远不会改变吗?看看下面的情形:

char *str = B;               // 调用B.operator char*()

strcpy(str, "Hi Mom");       // 修改str指向的值

B的值现在还是"Hello World"吗?或者,它是否已经变成了对母亲的问候语?答案完全取决于String::operator char*的实现。

下面是一个有欠考虑的实现,它导致了错误的结果。但是,它工作起来确实很高效,所以很多程序员才掉进它的错误陷阱之中:

// 一个执行很快但不正确的实现
inline String::operator char*() const
{ return data; }

这个函数的缺陷在于它返回了一个"句柄"(在本例中,是个指针),而这个句柄所指向的信息本来是应该隐藏在被调用函数所在的String对象的内部。这样,这个句柄就给了调用者自由访问data所指的私有数据的机会。换句话说,有了下面的语句:

char *str = B;

情况就会变成这样:

str------------------------->"Hello World/0"
              /   
             /
B.data

显然,任何对str所指向的内存的修改都使得B的有效值发生变化。所以,即使B声明为const,而且即使只是调用了B的某个const成员函数,B也会在程序运行过程中得到不同的值。特别是,如果str修改了它所指的值,B也会改变。

String::operator char*本身写的没有一点错,麻烦的是它可以用于const对象。如果这个函数不声明为const,就不会有问题,因为这样它就不能用于象B这样的const对象了。

但是,将一个String对象转换成它相应的char*形式是很合理的一件事,无论这个对象是否为const。所以,还是应该使函数保持为const。这样的话,就得重写这个函数,使得它不返回指向对象内部数据的句柄:

// 一个执行慢但很安全的实现
inline String::operator char*() const
{
  char *copy = new char[strlen(data) + 1];
  strcpy(copy, data);

  return copy;

}

这个实现很安全,因为它返回的指针所指向的数据只是String对象所指向数据的拷贝;通过函数返回的指针无法修改String对象的值。当然,安全是要有代价的:这个版本的String::operator char* 运行起来比前面那个简单版本要慢;此外,函数的调用者还要记得delete掉返回的指针。

如果不能忍受这个版本的速度,或者担心内存泄露,可以来一点小小的改动:使函数返回一个指向const char的指针:

class String {
public:
  operator const char *() const;

  ...

};

inline String::operator const char*() const
{ return data; }

这个函数既快又安全。虽然它和最初给出的那个函数不一样,但它可以满足大多数程序的需要。这个做法还和C++标准组织处理string/char*难题的方案一致:标准string类型中包含一个成员函数c_str,它的返回值是string的const char*版本。关于标准string类型的更多信息参见条款49。

指针并不是返回内部数据句柄的唯一途径。引用也很容易被滥用。下面是一种常见的用法,还是拿String类做例子:

class String {
public:

  ...

  char& operator[](int index) const
  { return data[index]; }

private:
  char *data;
};

String s = "I'm not constant";

s[0] = 'x';               // 正确, s不是const

const String cs = "I'm constant";

cs[0] = 'x';              // 修改了const string,
                          // 但编译器不会通知

注意String::operator[]是通过引用返回结果的。这意味着函数的调用者得到的是内部数据data[index]的另一个名字,而这个名字可以用来修改const对象的内部数据。这个问题和前面看到的相同,只不过这次的罪魁祸首是引用,而不是指针。

这类问题的通用解决方案和前面关于指针的讨论一样:或者使函数为非const,或者重写函数,使之不返回句柄。如果想让String::operator[]既适用于const对象又适用于非const 对象,可以参见条款21。

并不是只有const成员函数需要担心返回句柄的问题,即使是非const成员函数也得承认:句柄的合法性失效的时间和它所对应的对象是完全相同的。这个时间可能比用户期望的要早很多,特别是当涉及的对象是由编译器产生的临时对象时。

例如,看看这个函数,它返回了一个String对象:

String someFamousAuthor()           // 随机选择一个作家名
{                                   // 并返回之

  switch (rand() % 3) {             // rand()在<stdlib.h>中
                                    // (还有<cstdlib>。参见条款49)
  case 0:
    return "Margaret Mitchell";     // 此作家曾写了 "飘",
                                    // 一部绝对经典的作品
  case 1:
    return "Stephen King";          // 他的小说使得许多人
                                    // 彻夜不眠
  case 2:
    return "Scott Meyers";          // 嗯...滥竽充数的一个
  }                               
                                  

  return "";                        // 程序不会执行到这儿,
                                    // 但对于一个有返回值的函数来说,
                                    // 任何执行途径上都要有返回值
}

希望你的注意力不要集中在随机数是怎样从rand产生的问题上,也不要嘲笑我把自己和这些作家联系在一起。真正要注意的是,someFamousAuthor的返回值是一个String对象,一个临时String对象(参见条款M19)。这样的对象是暂时性的,它们的生命周期通常在函数调用表达式结束时终止。例如上面的情况中,包含someFamousAuthor函数调用的表达式结束时,返回值对象的生命周期也就随之结束。

具体看看下面这个使用someFamousAuthor的例子,假设String声明了一个上面的operator const char*成员函数:

const char *pc = someFamousAuthor();

cout << pc;                  

不论你是否相信,谁也不能预测这段代码将会做些什么,至少不能确定它会做些什么。因为当你想打印pc所指的字符串时,字符串的值是不确定的。造成这一结果的原因在于pc初始化时发生了下面这些事件:
1. 产生一个临时String对象用以保存someFamousAuthor的返回值。
2. 通过String的operator const char*成员函数将临时String对象转换为const char*指针,并用这个指针初始化pc。
3. 临时String对象被销毁,其析构函数被调用。析构函数中,data指针被删除(代码详见条款11)。然而,data和pc所指的是同一块内存,所以现在pc指向的是被删除的内存--------其内容是不可确定的。

因为pc是被一个指向临时对象的句柄初始化的,而临时对象在被创建后又立即被销毁,所以在pc被使用前句柄已经是非法的了。也就是说,无论想做什么,当要使用pc时,pc其实已经名存实亡。这就是指向临时对象的句柄所带来的危害。

所以,对于const成员函数来说,返回句柄是不明智的,因为它会破坏数据抽象。对于非const成员函数来说,返回句柄会带来麻烦,特别是涉及到临时对象时。句柄就象指针一样,可以是悬浮(dangle)的。所以一定要象避免悬浮的指针那样,尽量避免悬浮的句柄。

同样不能对本条款绝对化。在一个大的程序中想消灭所有可能的悬浮指针是不现实的,想消灭所有可能的悬浮句柄也是不现实的。但是,只要不是万不得已,就要避免返回句柄,这样,不但程序会受益,用户也会更信赖你。

高通Camx功能feature分析之十:高通NCS服务之注册和启动 本文介绍了高通NCS(非相机传感器)服务的注册与启动流程。NCS通过QMI接口访问陀螺仪等传感器,为相机EIS/AEC等功能提供辅助数据。文章详细解析了服务注册时的QueryCapabilites能力查询、RegisterService注册流程,以及通过StartNCSService启动服务线程的过程。重点包括:1)AF/EIS等节点通过NCSService创建传感器对象;2)注册时需校验传感器类型和接口;3)首次注册成功后会启动NCS服务线程;4)采样率变化时需重新配置会话。通过线程池管理实现传感器数据的 阅读详情

相关推荐

Windows下CMake编译VTK

选择项目INSTALL,生成该项目,得到vtk在Debug模式下的lib和dll文件,在文件夹中可看见,已得到debug模式下lib库与dll库。,把Value值设置为最后在vtk_sln中生成include、dll、lib的位置。在上面点击Configure后,红色区域没有了。选择自己vs编译平台的版本信息,我这边已经设置好了没有这个设置了,借用参考博客的图片。设置完后,点击左下角"Configure",然后等待Configure。,把搜索结果的Value勾选上。在search中搜索。

weixin_74239923的博客 842

易语言多线程封装线程启动返回句柄

封装多线程模块-线程启动 • 1、CreateThread • 2、线程_启动_句柄() • 3、线程_启动_逻辑() • 4、线程句柄 • 5、线程ID • 6、易语言SHCreateThread 511遇见易语言多线程大漠多线程 SHCreateThread function | Microsoft Docs https://docs.microsoft.com/zh-cn/windows/desktop/api/shlwapi/nf-shlwapi-shcreatethread .版本

511遇见 7443

ESP32开发板使用Arduino实现变成蓝牙鼠标教程

通过这个教程,你可以轻松将ESP32开发板变成蓝牙鼠标。如果你在使用过程中遇到任何问题,或者库没有正常工作,不妨下载我上传的修复版本,再试试看。希望你们能根据这个基础做出更多有趣的蓝牙控制项目!

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 970

句柄(HANDLE)的简单理解

文章目录什么是句柄句柄的本质理解为什么需要句柄 什么是句柄 句柄是一个整数,单独的看它只是数字。 但这个整数是进程句柄表数组的下标,有了这个下标,操作系统就可以找到其索引的数据结构,并能找到数据结构里面的指针,然后根据这个指针获取内核里的某个对象。 句柄的本质理解 操作系统内核中有一个全局句柄表(如上图)。每个进程有自己的一个句柄表,是一个数据结构组成的数组,每个数据结构包含一个指向内核对象的指针、访问掩码、继承标识等。 句柄是进程句柄表数组的下标。 在32位系统中,句柄是一个32位值。64位系统中则是

weixin_43399489的博客 1万+

SAP_SD模块更改销售订单定价简介

方式优点缺点适用场景MASS简单,无需开发不能触发重新定价逻辑修改特定字段BDC (VA02)可模拟人工操作维护复杂,速度慢少量订单批改稳定、安全需ABAP支持推荐用于批量重新定价VBOF标准重算返利仅适返利类条件返利更新通过本文,我们了解了 SAP 的[SAP_SD模块更改销售订单定价]的基本概念和应用方法。希望这些信息能帮助您在实际工作中更好地使用 SAP 系统,提高工作效率和管理水平。更多SAP文章请点击更多SAP文章更新,大家一起学习进步!!!

我小时候很黑的博客 715

RK3588 Buildroot 新建板级DTS

在rk3588 linux开发过程中,新创建板型主要基于硬件差异化、软件适配与优化、项目定制化等多方面需求。

501846585_QQAndroid_java 1299

c++ 避免返回内部数据的句柄

为什么const对象只能访问const成员函数 C Primer中说了,在一个对象调用其成员函数时,它隐含的一个形参this指针。 例如,我们定义了一个函数CTest::ttt();实际上在编译器中该函数的定义就是CTest::ttt(CTest *const this),该this指针所指向的内容可以改变,但是该this指针不可以被改变。当我们用CTest的一个对象test1调用

Deep learning and thinking 551

Apache DolphinScheduler 实现了对 OceanBase 的元数据库支持

Apache DolphinScheduler 是一款开源的分布式工作流任务调度系统,它提供了可视化的工作流设计和调度功能,支持大数据任务的自动化调度和监控。最近的更新中,Apache DolphinScheduler 引入了对 OceanBase 数据库的元数据库支持,这为用户在大数据环境中管理和调度任务提供了更多选择。用户可以通过 DolphinScheduler 提供的可视化界面进行工作流的设计和调度,也可以通过 DolphinScheduler 提供的 API 进行任务的编程调度。

VavzNvml的博客 495

条款29: 避免返回内部数据的句柄

假设b是一个const string对象: class string { public: string(const char *value); // 具体实现参见条款11 ~string(); // 构造函数的注解参见条款m5 operator char *() const; // 转换st...

weixin_30341735的博客 123

TRON 主网节点搭建-2024最新详细版文档

TRON主网节点搭建以前,大概说明一下本次使用的服务器配置,本次从搭建部署到节点完成同步的时间大约为36小时。

J_Lee 4060

Android性能分析深度指南:Perfetto工具全面解析

本文深入探讨Android性能优化工具Perfetto的应用与实践。作为Google推出的新一代追踪系统,Perfetto整合了CPU调度、内存分配、电池状态等多维度分析功能,通过高性能数据处理和可视化界面帮助开发者定位性能瓶颈。文章系统介绍了Perfetto的架构原理、配置方法和核心功能,包括CPU性能分析、内存泄漏检测等关键技术。通过电商应用卡顿优化的实战案例,展示了如何利用数学建模(如帧时间公式$T_{\text{render}}\leq16.67\text{ms}$)指导性能调优。

郑伟强dev的专栏 144

effective c++ item 25-29

effective c++

weixin_44609676的博客 449

线性代数-第24篇:线性代数在控制系统中的核心应用

线性代数通过状态空间建模、矩阵分解与优化算法,为控制系统提供了从理论分析到工程实现的完整框架。从传统PID控制到现代智能控制,其应用贯穿系统设计、分析与优化的全流程。未来,随着多智能体协同控制、量子控制系统等前沿领域的发展,线性代数将在高维矩阵计算、分布式优化等方向持续创新,为复杂控制系统的突破提供核心支撑。如果对线性代数在控制系统中的应用细节有疑问,欢迎在评论区留言!

无 424

[翻译] Effective C++, 3rd Edition, 附录 B. 第二和第三版之间的 Item 映射

附录 B. 第二和第三版之间的 Item 映射作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/Effective C++ 的第三版在很多方面与第二版不同,其中最引人注目的是它包含很多新的信息。然而,第二版的大部分内容依然保留在第三版中,虽然经常会改变形式和位置

艺术编程 5819

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

【Effective C++】 Item 1 View C++ as a federation of language 中英自译

当看完这一节,开始对C++有一点更深的了解。为什么C++这么难去学会,掌握?这里Scott Meyers讲到——View C++ as a federation of language(将C++视为一个语言联邦),看着这个标题我也是各种不懂的= =,但看下去,便会发现如何去理清C++的大方向了。     In the beginning, C++ was just C with some obj

jason__ye的专栏 536

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Effective C++----3rd Edition, Item 1:将C++视为语言的联合体

Item 1: 将 C++ 视为 federation of languages(语言联合体) 作者:Scott Meyers 译者:fatalerror99 (iTePub's Nirvana) 发布:http://blog.youkuaiyun.com/fatalerror99/ 最初,C++ 仅仅是在 C 的基础上附加了一些 object-oriented(面向对象)的特性。C+

烽火前秦路 460

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

more effective c++——Item M29 引用计数(三)带引用计数的基类的实现

带引用计数的基类实现:Cstring类的实现与通过增加中间层来为不可修改源代码的实现引用计数功能

卢然小子的博客 812

[翻译] Effective C++, 3rd Edition, Item 1: 将 C++ 视为语言的联合体

Item 1: 将 C++ 视为 federation of languages(语言联合体)作者:Scott Meyers译者:fatalerror99 (iTePubs Nirvana)发布:http://blog.youkuaiyun.com/fatalerror99/最初,C++ 仅仅是在 C 的基础上附加了一些 object-oriented(面向对象)的特性。C++ 最初

艺术编程 7360

读书笔记 effective c++ Item 49 理解new-handler的行为

1. new-handler介绍 当操作符new不能满足内存分配请求的时候,它就会抛出异常。很久之前,它会返回一个null指针,一些旧的编译器仍然会这么做。你仍然会看到这种旧行为,但是我会把关于它的讨论推迟到本条款结束的时候。 1.1 调用set_new_handler来指定全局new-handler 在operator new由于不能满足内存分配要求而抛出异常之前,它会调用一个客户指定的叫...

HarlanC的专栏 165

Effective c++ Item 28 不要返回指向对象内部数据(internals)的句柄(handles)

假设你正在操作一个Rectangle类。每个矩形可以通过左上角的点和右下角的点来表示。为了保证一个Rectangle对象尽可能小,你可能决定不把定义矩形范围的点存储在Rectangle类中,而是把它放入一个辅助结构体中,Rectangle中声明一个指向它的指针就可以了: 1 class Point { // class for repre

小小小程序员的博客 218

读书笔记 effective c++ Item 43 了解如何访问模板化基类中的名字

1. 问题的引入——派生类不会发现模板基类中的名字 假设我们需要写一个应用,使用它可以为不同的公司发送消息。消息可以以加密或者明文(未加密)的方式被发送。如果在编译阶段我们有足够的信息来确定哪个信息会被发送到哪个公司,我们可以使用基于模板的解决方案: 1 class CompanyA { 2 public: 3 ... 4 void sendCleartext(cons...

weixin_34406086的博客 116

Effective C++ 第3版 Item 26详尽研究 个人认为最后一些内容有待商酌

Effective C++ 第3版 Item 26详尽研究 个人认为最后一些内容有待商酌 Postpone variable definitions as long as possible? 真的应该这样? 当然,个人承认前面的一部分都没有错,而且是一贯的很对,很有道理,最后作者提到在循环的问题上应该怎么处理,他提出了两个Aproach. Approach A: define outsi...

webcenterol 140

读书笔记 effective c++ Item 10 让赋值运算符返回指向*this的引用

一个关于赋值的有趣的事情是你可以将它们链在一起: 1 int x, y, z; 2 3 x = y = z = 15; // chain of assignments 同样有趣的是赋值采用右结合律,所以上面的赋值链被解析成下面这个样子: 1 x = (y = (z = 15)); 在这里,15被赋值给z,然后赋值的结果(更新的z)被赋值给y,再然后赋值的结果(更新的Y)被赋值给x。 ...

HarlanC的专栏 152
上一篇: Effective C++ 2e Item28
下一篇: Effective C++ 2e Item30
lostmouse
博客等级 码龄26年 22粉丝 63原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值