gpt4 book ai didi

c++ - 给定一个 C++ 嵌套私有(private)结构类型,是否有从文件范围静态函数访问它的策略?

转载 作者:行者123 更新时间:2023-12-01 14:52:10 35 4
gpt4 key购买 nike

有人可以描述作者 John Lakos 在以下引用中提到的精确编码策略吗?

约翰·拉科斯:

More controversially, it is often better to have two copies of a struct—e.g., one nested/private in the .h file (accessible to inline methods and friends) and the other at file scope in the .cpp file (accessible to file-scope static functions) —and make sure to keep them in sync locally than to pollute the global space with an implementation detail.

引用出现在较新的 Lakos 大部头,Large Scale C++ .

(这本书是对与 Lakos 早期著作相同主题的更新处理,Large Scale C++ Software Design)

引用在第 9 页的第 0.2 节中。

如果后面的章节清楚 Lakos 描述的内容,我会返回这里并发布答案。

与此同时,我对理解这句话着迷了,我试图浏览本书的目录和索引以寻找更多线索,但尚未找到答案。

下面是我自己的示例代码,我想象会用神秘的策略解决这个问题:

// THE HEADER

namespace project
{
class OuterComponent
{
public:
inline int GetFoo()
{
return m_inner.foo;
}

int GetBar();

private:
struct InnerComponent
{
int foo = 0;
int bar = 0;
};

InnerComponent m_inner;
};
} // namespace project

连同:

// THE CPP IMPLEMENTATION FILE

namespace project
{
namespace
{
/*
MYSTERY:
Per the book quotation, I can somehow add a "copy of InnerComponent" here?
And "make sure to keep them in sync locally"?
*/

// COMPILATION ERROR (see below)
int FileScopeComputation( OuterComponent::InnerComponent i )
{
return i.bar - 3;
}
} // namespace

int OuterComponent::GetBar()
{
return FileScopeComputation( m_inner );
}

} // namespace project

当然,上面的不会编译。

错误将类似于:

error: ‘struct project::OuterComponent::InnerComponent’ is private within this context
int FileScopeComputation( OuterComponent::InnerComponent i )
^~~~~~~~~~~~~~

名为 FileScopeComputation 的自由函数无法访问 InnerComponent,原因我很清楚。

将上述代码与书名联系起来

回到 Lakos 的引述,我的想法是 FileScopeComputation 是引述所称的 “文件范围静态函数” 的一个实例。

使代码编译的一个“明显”解决方案是移动 InnerComponent,以便它在 OuterComponentpublic 部分中声明。

但是,我认为我的“显而易见”的解决方案是有罪的(根据引文)[污染] 了实现细节的全局空间。”

因此,我的代码似乎同时包含了:(a) 所提及策略的目标和 (b) 一种潜在解决方案的不必要污染。那么替代解决方案是什么?

请回答其中一个或两个:

(1) 有没有办法在 cpp 文件中制作另一个 struct InnerComponent 的拷贝,这样字段 OuterComponent::m_inner 保持私有(private),类型 OuterComponent::InnerComponent 也保持私有(private),然而函数 FileScopeComputation 有某种方式做一些“等同于”访问 InnerComponent 实例上的数据?

我尝试过一些奇怪的选角方法,但没有什么看起来值得在书中推荐的。同时,根据我的经验,Lakos在书上推荐的东西,都是非常值得推荐的。

(2) 我是否完全误读了引用适用于哪种场景?如果是这样,那么引用实际上指的是什么 C++ 软件设计问题?还有什么其他问题涉及“结构的两个拷贝...一个在 h 文件中...另一个在 cpp 文件中”

更新:

基于perceptive answer by dfri ,上面的代码确实可以通过最小的更改进行编译,这样:

  • OuterComponent::m_inner 字段保持私有(private)
  • OuterComponent::InnerComponent 类型也保持私有(private)
  • 然而 FileScopeComputation 函数有一些方法可以访问 InnerComponent 实例上的数据

头代码多了一行代码:

...
private:
struct InnerUtil; // <-- this line was ADDED. all else is same as above.
struct InnerComponent
{
int foo = 0;
int bar = 0;
};

InnerComponent m_inner;
};

cpp文件代码变为:

struct OuterComponent::InnerUtil
{
static int FileScopeComputation( OuterComponent::InnerComponent i )
{
return i.bar - 3;
}
};

int OuterComponent::GetBar()
{
return InnerUtil::FileScopeComputation( m_inner );
}

最佳答案

因式分解模式 (Lakos)

有可能 Lakos 实际上指的是公共(public) API 委托(delegate)调用的单独命名的类型。引用和拉科斯之间有一种相似的感觉Factoring pattern (“Imp 和 ImpUtil 模式”),尤其是“ImpUtil”部分。

struct A {};
struct B {};
struct C {};

// widgetutil.h
// (definitions placed in widgetutil.cpp)
struct WidgetUtil {
// "Keep API in sync with Widget::foo".
static void foo(const A& b, const B& c, const C& a) {
// All implementation here in the util.
(void)a; (void)b; (void)c;
}

// "Keep API in sync with Widget::bar".
static void bar(const B& b, const C& c) {
// All implementation here in the util.
(void)b; (void)c;
}
};

// widget.h
// includes "widgetutil.h"

// Public-facing API
// (Ignoring the Imp pattern, only using the Util pattern).
struct Widget {
void foo(const A& a, const B& b) const {
// Only delegation to the util.
WidgetUtil::foo(a, b, c_);
}

void bar(const B& b) const {
// Only delegation to the util.
WidgetUtil::bar(b, c_);
}

private:
C c_{};
};

int main() {
const Widget w;
w.foo(A{}, B{}); // --> WidgetUtil::foo
}

这是一种将实现细节 ( WidgetUtil ) 与面向公众的 API ( Widget ) 分开的方法,同时也有助于测试:

  • WidgetUtil 的单元测试中对实现细节进行了单独单元测试,
  • 测试 Widget可以执行而无需担心 WidgetUtil 中的副作用成员,因为后者可以在 Widget 中使用静态依赖注入(inject)完全模拟(使其成为类模板)。

如果我们回顾 Lakos 的引述(在 OP 中),WidgetUtil也可以作为文件范围类放置在 widget.cpp 中源文件,隐藏在公共(public) API 之外。这将意味着更多的封装,但不会促进与上述分离相同的测试。

最后,注意制作Widget类模板并不一定意味着污染 widget.h 的用户的翻译单元带有定义(需要在每个 TU 中编译,包括和实例化 Widget )。作为Widget被制作成一个类模板以方便测试,它的产品实现将永远只使用一个实例,即注入(inject)产品WidgetUtil .这意味着可以将 Widget 的定义分开类模板成员函数的声明,就像非模板类一样,并显式实例化 Widget<WidgetUtil>特化widget.cpp .例如。使用以下方法:

  • 将成员函数的定义分开类模板 Widget来自类模板的头文件定义,到一个单独的-timpl.h头文件,
  • -timpl.h头文件又包含在关联的 .cpp 中文件,该文件又包含 Widget 的显式实例化生产中使用的单一类型模板参数的类模板,即 WidgetUtil .

测试可以类似地包括 -timpl.h header 并实例化 Widget改为使用模拟的 util 类的类模板。

关于c++ - 给定一个 C++ 嵌套私有(private)结构类型,是否有从文件范围静态函数访问它的策略?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62616360/

35 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com