打破封装是个好主意吗

Is it ever a good idea to break encapsulation?

本文关键字:好主意 封装      更新时间:2023-10-16

我刚开始学习封装,我偶然发现std::string使用的两个函数似乎破坏了它的封装。

关于中的c_str()和data()http://www.cplusplus.com/reference/string/string/c_str/和http://www.cplusplus.com/reference/string/string/data/

"返回的指针指向字符串对象当前用于存储符合其值的字符的内部数组"。

对于刚刚学习OO编程的人来说,打破封装是个好主意吗?给一个更高级的人怎么样?

顺便说一句,这似乎是与C++98不同的行为。你为什么相信他们做出了这些改变?

谢谢你抽出时间。

虽然有时实用性和向后兼容性会覆盖对封装的渴望,就像Mahmoud提到的那样,但不要让C++标准库成为轻易破坏封装的验证。这一点一直存在争议,甚至是许多代码库中错误的来源。c_str的问题是,在修改被认为是未定义行为的字符串后,人们滥用返回的指针值或长时间保留它,会导致内存损坏,但编译器和运行时环境都无法强制执行该限制,因此在这种情况下,C++委员会选择了方便而非安全,在没有充分理由的情况下,不应作出这种权衡。

我偶然发现std::string使用的两个函数似乎破坏了它的封装

您的示例没有违反封装规则:

C++编程语言,第四版,Bjarne Stroustrup:

2.5.指向功能的指针

字符串没有隐式转换为char*。那是尝试过的在许多地方,发现容易出错。相反,标准库提供了显式转换函数c_str()到constchar*。

同样适用于string::data()。这意味着STL为您提供了一个谨慎的只读接口,用于提取存储在std::string中的数据。这并不违反封装——内部数据仍然隐藏在一个谨慎的接口后面,无法修改。如果存储在字符串对象中的char的内部数组被公开,或成为全局命名空间的一部分,或通过stringchar*的隐式转换(反之亦然),则会违反封装。

话虽如此:

打破封装是个好主意吗?

如果您的目标是在现实世界中创建可工作的应用程序,那么虔诚地遵循任何编程模型从来都不是一个好主意。

离开课堂后,将每个编程范式、模型和方法等制定的每个编程"规则"视为指南最佳实践

极端示例:您已经将一个复杂的应用程序部署到生产中,并且出现了一个bug。你有一个小时的时间来修复这个错误,或者你失去了工作,你的公司失去了一个客户。(是的,确实会发生这样的情况-已经存在了,做到了…)。你可以快速修复会违反封装规则的问题,但会在半小时内让你的系统重新启动并运行。或者,为了遵守封装规则,您可以花两天时间重构应用程序,仔细修改500行代码,将新版本部署到测试组,并有望在两周内准备好补丁版本。该怎么办?

答案很清楚:至少目前,你将打破封装规则,进行快速而肮脏的修复,让你的系统重新启动并运行。

一旦处理好了,你可以坐下来,仔细思考,与同事和经理讨论,并决定抽出两周时间来维持封装规则是否确实有显著的投资回报率。

你可以确信一件事:如果你在一个商业环境中工作,人们通过提供可工作的软件来谋生,那么决策将不是由某些教科书中概述的OOP规则决定的,而是由企业的底线决定的。

编程的问题是,你永远不会在自己的世界里只处理自己的代码。您必须编写代码来将各种部件和组件组合在一起,并且您的代码需要能够做到这一点。

毫无疑问,string非常棒。但它们是一种抽象——它们提供了一种很好、优雅、简单、有用的方式来创建和交互表示内存中文本数据的字节。一天下来,就像string一样令人敬畏和精彩,它可以归结为文本。ASCII、UTF-8等等,都是文本。如果每个人都能使用std::string,你可以用std::string和我说话,我可以用std::string和你说话,我们可以幸福快乐地生活在一起,那就太好了。但不幸的是,我们生活在现实世界中,事实并非如此。

迟早,您会发现自己正在与期望纯文本的C API进行集成。(什么是纯文本?没有人会同意。但基本上,指针指向内存中某个地方的ASCII/UTF8/UTF16/etc编码的字节数组)。他们会要求一个const char *data,而你所拥有的只是你的花式聊天std::string。哦,没有。

这时你会意识到你的封装很好,但为了让你的代码真正地做一些事情有用,为了你和其他人,你需要能够以通用的数据格式访问它。因此,您将开发一个小的助手成员函数.c_str().c_int(),它将使您的精致封装类的核心功能变得可访问,这样人们就可以根据需要从中读取/写入,而不需要被迫使用您长期努力制作的相同封装技术。

特别是在处理诸如整数和字符数组之类的原始数据类型时,您会发现即使API开发人员使用与您相同的语言/工具,他们也经常会避免使用华丽的封装类型。当您发现C++API接受或返回char *而不是std::string时,不要感到惊讶。有时他们有很好的理由(祝你好运,尝试用一个编译器/标准库编译std::string,使其真正正确匹配,而不会将你的系统的检查与用不同的、不兼容ABI的库编译的代码分段!),有时他们想将API"简化"到最低限度,有时他们会毫无理由。