【问题标题】:static class data vs. anonymous namespaces in C++C++ 中的静态类数据与匿名命名空间
【发布时间】:2009-05-13 13:02:03
【问题描述】:

我偶尔会遇到带有私有静态数据成员的类。我目前正在争论是否应该将这些替换为实现文件中未命名命名空间中的静态变量。除了不能在内联方法中使用这些变量之外,还有其他缺点吗?我看到的优点是对班级的用户完全隐藏了它们。

【问题讨论】:

    标签: c++ namespaces static-members


    【解决方案1】:

    我不相信这种好处值得可读性影响。我通常认为私密的东西“足够隐蔽”。

    【讨论】:

      【解决方案2】:

      1) 存在对二进制组织增加限制的形式的缺点。在 1990 年代 C++ 会议上的一次演讲中,Walter Bright 报告说通过组织他的代码使相互调用的函数在同一个编译单元中实现了显着的性能提升。例如,如果在执行期间 Class1::method1 对 Class2 方法的调用远多于对其他 Class1::methods 的调用,则在 class2.cpp 中定义 Class1::method1 意味着 Class1::method1 将与方法位于同一代码页上它正在调用,因此不太可能因页面错误而延迟。这种重构使用类静态比使用文件静态更容易进行。

      2) 反对引入命名空间关键字的论据之一是“你可以对类做同样的事情”,你会看到类和结构被用作穷人的命名空间来自前命名空间时代的来源。令人信服的反驳是因为命名空间是可重新打开的,并且任何地方的任何功能都可以使自己成为命名空间的一部分或访问外部命名空间,那么你可以用命名空间做一些你可以 做一堂课。这与您的问题有关,因为它表明语言委员会认为命名空间范围非常类似于类范围;这减少了在使用匿名命名空间而不是类静态时存在一些微妙的语言陷阱的机会。

      【讨论】:

      • 请记住,那是在 90 年代,编译器已经发展到 VC++ 的“整个程序优化”之类的东西运行良好的程度,以至于这种重构是在浪费程序员的时间。
      • Walter Bright 是否说过这种重构现在是浪费时间?在那次会议上,解决可维护性问题的问题之一是“除了你之外,还有谁能够在你退休后做这些事情?”沃尔特对此的回答是“当我退休时,编译器会变慢。”
      【解决方案3】:

      我不同意其他答案。尽量远离课堂 尽可能定义。

      Scott Meyers' Effective C++ 3rd edition 他建议首选非朋友 类方法的函数。这样,类定义为 尽可能小,在尽可能少的地方访问私有数据 可能(封装)。

      遵循这一原则进一步导致pimpl idiom。然而, 需要平衡。确保您的代码是可维护的。盲目地, 遵循此规则将导致您使用所有私有方法 本地文件,只需将所需的成员作为参数传递。这 会改进封装,但会破坏可维护性。

      话虽如此,文件本地对象很难进行单元测试。和 一些聪明的黑客攻击你可以在单元测试期间访问私人成员。 访问文件本地对象有点多involved

      【讨论】:

        【解决方案4】:

        它不仅对课程的用户隐藏它们,而且对你隐藏它们!如果这些变量是类的一部分,它们应该以某种方式与类相关联。

        根据您要对它们做什么,您可以考虑在静态成员函数中将它们设为静态变量:

        // header file
        class A {
          public:
             static void func();
        };
        
        
        // cpp file
        void A :: func() {
            static int avar = 0;
            // do something with avar
        }
        

        【讨论】:

          【解决方案5】:

          我想这归结为这些变量在类的上下文中是否具有某些实际含义(例如,指向所有对象使用的一些公共内存的指针)或者只是一些您需要在方法之间传递的临时数据,并且宁愿不要把课堂弄得乱七八糟。在后一种情况下,我肯定会使用未命名的命名空间。在前者中,我会说这是个人品味的问题。

          【讨论】:

            【解决方案6】:

            我部分同意 Greg 的观点,因为未命名的命名空间成员并不比私有数据更封装。毕竟,封装的目的是向其他模块隐藏实现细节,而不是向其他程序员隐藏。不过,我确实认为在某些情况下这是一种有用的技术。

            考虑如下类:

            class Foo {
            public:
                ...
            private:
                static Bar helper;
            };
            

            在这种情况下,任何想要使用 Foo 的模块也必须知道 Bar 的定义,即使该定义与以往无关。这些头文件依赖导致更频繁和更长的重建。将 helper 的定义移动到 Foo.cpp 中的未命名命名空间是打破这种依赖关系的好方法。

            我也强烈反对未命名命名空间比静态数据成员更不可读或更难维护的观点。从标题中删除不相关的信息只会使其更简洁。

            【讨论】:

              猜你喜欢
              • 2010-09-14
              • 1970-01-01
              • 2011-12-09
              • 2012-11-20
              • 2010-11-23
              • 1970-01-01
              • 2010-11-28
              相关资源
              最近更新 更多