C 中带有 const 的“私有”结构成员

2024-03-28

为了拥有干净的代码,使用一些 OO 概念可能很有用,即使在 C 中也是如此。 我经常编写由一对 .h 和 .c 文件组成的模块。问题是模块的用户必须小心,因为 C 中不存在私有成员。使用 pimpl 习惯用法或抽象数据类型是可以的,但它添加了一些代码和/或文件,并且需要较重的代码。当我不需要访问器时,我讨厌使用访问器。

这里有一个想法,它提供了一种方法,让编译器抱怨对“私有”成员的无效访问,只需一些额外的代码。这个想法是定义两次相同的结构,但为模块的用户添加一些额外的“const”。

当然,通过演员阵容仍然可以在“私人”成员中写作。但重点只是为了避免模块用户的错误,而不是安全地保护内存。

/*** 2DPoint.h module interface ***/
#ifndef H_2D_POINT
#define H_2D_POINT

/* 2D_POINT_IMPL need to be defined in implementation files before #include */
#ifdef 2D_POINT_IMPL
#define _cst_
#else
#define _cst_ const
#endif

typedef struct 2DPoint
{
    /* public members: read and write for user */
    int x;
    
    /* private members: read only for user */
    _cst_ int y;
} 2DPoint;

2DPoint *new_2dPoint(void);
void delete_2dPoint(2DPoint **pt);
void set_y(2DPoint *pt, int newVal);


/*** 2dPoint.c module implementation ***/
#define 2D_POINT_IMPL
#include "2dPoint.h"
#include <stdlib.h>
#include <string.h>

2DPoint *new_2dPoint(void)
{
    2DPoint *pt = malloc(sizeof(2DPoint));
    pt->x = 42;
    pt->y = 666;

    return pt;
}

void delete_2dPoint(2DPoint **pt)
{
    free(*pt);
    *pt = NULL;
}

void set_y(2DPoint *pt, int newVal)
{
    pt->y = newVal;
}

#endif /* H_2D_POINT */


/*** main.c user's file ***/
#include "2dPoint.h"
#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    2DPoint *pt = new_2dPoint();

    pt->x = 10;     /* ok */
    pt->y = 20;     /* Invalid access, y is "private" */    
    set_y(pt, 30);  /* accessor needed */
    printf("pt.x = %d, pt.y = %d\n", pt->x, pt->y);  /* no accessor needed for reading "private" members */

    delete_2dPoint(&pt);
    
    return EXIT_SUCCESS;
}

现在,问题是:这个技巧符合 C 标准吗? 它与 GCC 一起工作得很好,并且编译器不会抱怨任何事情,即使有一些严格的标志,但我如何确定这真的没问题?


这几乎肯定是未定义的行为。

写入/修改声明为的对象const被禁止,这样做会导致 UB。此外,您采取的方法重新声明struct 2DPoint作为两种技术上不同的类型,这也是不允许的。

请注意,这(通常是未定义的行为)并不意味着它“肯定不会工作”或“它必须崩溃”。其实我觉得还蛮逻辑上它有效,因为如果一个人明智地阅读来源,他可以很容易地找出它的目的是什么以及为什么它可能被认为是正确的。然而,编译器并不智能——充其量,它是一个有限自动机,不知道代码是什么supposed去做;它只(或多或少)遵守语法的句法和语义规则。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C 中带有 const 的“私有”结构成员 的相关文章

  • 仅在 Azure Web 应用程序中从 FTP 下载文件失败

    我有一个非常基本的代码 可以从 FTP 服务器下载文本文件列表 foreach var fileUri in files try var ftpRequest FtpWebRequest FtpWebRequest Create fileU
  • 为什么 Blazor 模板使用工厂来创建 HttpClient 实例

    当处置HttpClient 可能会遇到 套接字耗尽 的情况problem https learn microsoft com en us dotnet architecture microservices implement resilie
  • 您应该通过属性访问同一类中的变量吗?

    如果您有一个获取和设置实例变量的属性 那么通常您总是使用该类外部的属性来访问它 我的问题是你也应该在课堂上这样做吗 如果有的话 我总是使用该属性 即使是在班级内 但我想听到一些支持和反对的论据 以确定哪个是最正确的以及为什么 或者这只是项目
  • 为什么 lambda 可以将函数调用转换为 Actions?

    在此代码片段中 List
  • 将 Azure Blob 与 Azure 网站连接

    我正在尝试将 Azure 网站连接到 Azure blob 我打算在容器中托管一些文件 然后从我的网站获取它们 我从本教程开始 http azure microsoft com en us documentation articles we
  • 如何通过特定的行分隔符读取文本文件?

    使用流读取器读取文本文件 using StreamReader sr new StreamReader FileName Encoding Default string line sr ReadLine 我想强制行分隔符应该是 n not
  • (简单)boost thread_group 问题

    我正在尝试编写一个相当简单的线程应用程序 但我对 boost 的线程库很陌生 我正在开发的一个简单的测试程序是 include
  • 具有自定义字段名称的 RavenDB 查询索引

    我在 RavenDB 中收集了 Message 文档 定义 class Message string Content Tag Tags class Tag string Value 我有索引 from doc in docs Message
  • 让子进程等待直到收到父进程的信号

    我想从父级创建 N 个子级 我希望所有的孩子同时开始 一个功能 测量时间 因此 我将该函数放入信号处理程序中 当父级完成创建 分叉 所有子级时 它会向所有子级发送信号 使用kill children id 以让 make 开始 代码如下 但
  • C#等待串口数据

    我试图通过 C 应用程序从指纹扫描仪获取数据 但在指纹发送之前 我的整个代码都会执行 我尝试使用延迟功能System Threading Thread Sleep 1000 因此它可以在下一步执行之前获取数据 但这一切似乎都是徒劳的 任何人
  • C# 从视频文件的一部分中提取帧

    使用 AForge ffmpeg 包装器 您可以使用 VideoFileReader 类从视频中提取帧并将其保存为位图 请参阅以下示例 提取 avi 文件的帧 https stackoverflow com questions 178256
  • 如何在 Mac 上的 Sublime Text 2 上运行 C++?

    我尝试在 Mac 上的 Sublime Text 2 上用 C 运行 hello world I typed include iostream int main cout lt lt Hello WOrld return 0 但这给了我一个
  • IE 中“对象不支持属性或方法‘查找’”

  • 如何分配二维数组? [复制]

    这个问题在这里已经有答案了 我需要创建一个二维数组 目前我将其创建为int a 100 100 但我需要使用动态分配内存malloc在C语言中 我用了代码 include
  • Python 比 C++ 更快、更轻吗? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • 什么是排列索引?

    我正在阅读 加速 C 我不明白练习 5 1 设计并实现一个程序 根据以下输入生成排列索引 排列索引是其中每个短语由短语中的每个单词索引的索引 The quick brown fox jumped over the fence The qui
  • 让 clang-tidy 修复头文件

    我正在将当前使用 gcc 编译的项目移至 clang 并有一堆 gcc 没有生成的警告 Winconsistent missing override clang tidy致力于修复这些错误 cpp文件 但是它不触及hpp文件 因为在数据库中
  • 尝试返回 IQueryable 时发生转换错误

    我有一个查询应该返回IQueryable
  • qt 读取就绪信号

    我正在尝试与运行 1996 年处理器的设备建立串行连接 这意味着数据传输回我可能需要几秒钟的时间 我知道readyRead每次有新数据可用时都会生成信号 但我的问题是生成多长时间 这也是我可以测试就绪读取是否较低的一种方法 因为如果当它们不
  • WPF 中的 InvokeRequired [重复]

    这个问题在这里已经有答案了 我在一个中使用了这个函数Windows forms应用 delegate void ParametrizedMethodInvoker5 int arg private void log left accs in

随机推荐

  • opencv中的骨架化

    opencv中有没有可以使用的特定函数来进行骨架化 另外 我搜索了一段时间 想知道opencv是否实现了voronoi 骨骼 似乎有 但真的是 看起来 Voronoi Skeleton 是在 cvConstructSkeleton and
  • HTML5网络存储:不同网站可以互相覆盖用户计算机上的数据吗?

    我有一些关于 HTML5 存储概念的问题 我浏览了 w3c 规范 书籍和教程 但我仍然对某些概念有点不清楚 假设我访问网站 A 一些 JavaScript 在我的浏览器中运行 用于设置键值对 例如 username deepak 然后我访问
  • 从选择标签中的数据库获取值 HTML PHP MySQL [重复]

    这个问题在这里已经有答案了 我是 PHP MySQL 的初学者 我成功地从数据库中获取了值INPUT类型 但我无法从数据库中获取数据SELECT type 这是我的示例编辑表单 其中Gender 用户类型无法从我的数据库输出值 and he
  • 如何将雪花中单词的第一个字母大写?

    我需要在 Snowflake 中将 mySQL 查询中某些单词的首字母大写 我目前正在使用这个功能 SELECT case when FLAG1 is null then upper FLAG2 else FLAG1 END as STAT
  • 为什么新的 gradle 测试过滤功能不适用于我的构建脚本?

    我正在开发一个具有以下结构的 项目 proj dbfit junit module db1 db2 提供一些背景信息 所有这些 模块 db1 db2 都有 JUnit 测试 这些测试使用 FitNesseRunner 将它们集成到 Bamb
  • 某些图像无法在 IE7 或 IE8 中显示

    我不明白为什么这个图像 以及其他类似的图像 不会在 IE7 或 IE8 中显示 它在 IE9 Chrome Safari Firefox Mac 或 PC 以及这些浏览器的多个版本中完美显示 http images appletree co
  • Google Cloud CDN 开始忽略存储桶的查询字符串

    几个月前 为存储桶激活了 Cloud CDN 我们的存储数据会通过后端定期更改 因此 为了使缓存版本无效 我们添加了一个查询参数changedDate到提供给客户端的 url 当时这很有效 在过去几个月 可能是几周 的某个时候 谷歌似乎改变
  • 如何在 Python 中使用日期时间和 scipy 峰值绘制绘图?

    我从一个问题中举了一个例子 并将其适应我的数据集 但当涉及到绘制绘图时 我陷入了困境 我知道如何制作日期时间 值图 但我不知道如何进行组合 我得到的答复如下 Response https datascience stackexchange
  • 部署套件建议[关闭]

    很难说出这里问的是什么 这个问题是含糊的 模糊的 不完整的 过于宽泛的或修辞性的 无法以目前的形式得到合理的回答 如需帮助澄清此问题以便重新打开 访问帮助中心 help reopen questions 我在 Net 4 0 下编写了一些应
  • iOS 8 代码适用于 iPhone 5s,但不适用于 iPhone 5

    在 iPhone 5s 模拟器上一直测试我的 spritekit 游戏后 我终于尝试在 iPhone 5 模拟器上运行它 不幸的是 当我第一次触摸时 我就收到了一个我不明白的错误 我的touchesBegan函数调用我的addCoin函数
  • React router dom将数据从父组件传递到子路由器组件不传递props.match

    父组件App js的内容 import React useEffect useState from react import NavBar from components NavBar import Signup from pages Si
  • 如何使 formControl 只读

    如何以角度只读方式制作表单控件 我知道我可以用 html 来做
  • Numpy 索引重新排序数组

    我刚刚偶然发现了一个我不太理解的 numpy 索引行为 看起来 numpy 正在根据索引模式改变我的轴的顺序 不幸的是 我在文档中找不到以下内容的解释 有人可以向我解释一下发生了什么事吗 This is expected dimension
  • SignalR 响应覆盖标头

    我构建了一个位于 WebAPI 服务中的简单 SignalR 中心 并在 WebAPI 和 SignalR 上包含了所有必需的 CORS 属性 我的 WebAPI 端点都按预期工作 但 SignalR 却没有 我已经尝试了我能想到的所有方法
  • C中使用函数计算数组长度

    我想创建一个函数来计算传递数组的大小 我将传递一个数组作为输入 它应该返回其长度 我想要一个函数 int ArraySize int Array Or int Array Calculate Length of Array and Retu
  • Dash Python 应用程序按钮用于操作和刷新页面

    Dash App 中需要有一个回调函数来执行某些操作然后刷新页面 只能使用 HTML A 标签实现页面重新加载 html A html Button Refresh Data href 必需的 app layout html Div htm
  • 为什么 std::vector::insert 需要复制赋值?

    我试图理解以下行为 include
  • 在 Python 中使用 Nan 对图像进行高斯滤波

    根据 2D 坐标列表和第三个变量 速度 我创建了一个覆盖整个采样区域的 2D numpy 数组 我的目的是创建一个图像 其中每个像素包含位于其中的点的平均速度 之后用高斯滤波器过滤该图像 问题是该区域没有均匀采样 因此我有几个没有信息的像素
  • iOS:当应用程序因崩溃而退出时,是否有任何委托方法

    当我的应用程序因内存不足 内存泄漏等一般崩溃而崩溃时退出时 我想与我的服务器进行一些交互 我想知道 在这种情况下是否会调用任何委托方法 以便我可以在应用程序因任何崩溃而退出之前快速联系我的服务器 谢谢 正如您所解释的 您需要联系服务器 您可
  • C 中带有 const 的“私有”结构成员

    为了拥有干净的代码 使用一些 OO 概念可能很有用 即使在 C 中也是如此 我经常编写由一对 h 和 c 文件组成的模块 问题是模块的用户必须小心 因为 C 中不存在私有成员 使用 pimpl 习惯用法或抽象数据类型是可以的 但它添加了一些