C++命名空间和模板

7

我有一些可以分组的函数,但它们不属于任何对象/实体,因此不能被视为方法。

所以,在这种情况下,我会创建一个新的命名空间,并将定义放在header文件中,将实现放在cpp文件中。此外(如果需要),我会在该cpp文件中创建一个匿名命名空间,并将所有不必公开/包含到我的命名空间接口中的其他函数放在那里。

请参见下面的代码(可能不是最佳示例,可以通过另一种程序架构更好地完成,但我只是想不出更好的样本...)

示例代码(header

namespace algorithm {
   void HandleCollision(Object* object1, Object* object2);
}

示例代码(cpp

#include "header"

// Anonymous namespace that wraps 
// routines that are used inside 'algorithm' methods
// but don't have to be exposed
namespace {
   void RefractObject(Object* object1) {
      // Do something with that object
      // (...)
   }
}

namespace algorithm {
   void HandleCollision(Object* object1, Object* object2) {
      if (...) RefractObject(object1);
   }
}

目前为止还不错。我猜这是管理我的代码的好方法,但如果我有一些基于模板的函数并想做基本相同的事情,我不知道该怎么办。

如果我使用模板,我必须把所有的代码放在header文件中。好的,但是我应该如何隐藏一些实现细节呢?

我想要从我的接口中隐藏RefractObject函数,但我不能简单地删除它的声明(因为我把所有的代码都放在一个header文件中)...

我想到的唯一方法是:

示例代码(header

namespace algorithm {
   // Is still exposed as a part of interface!
   namespace impl {
      template <typename T>
      void RefractObject(T* object1) {
         // Do something with that object
         // (...)
      }
   }

   template <typename T, typename Y>
   void HandleCollision(T* object1, Y* object2) {
      impl::RefractObject(object1);
      // Another stuff
   }
}

有什么想法可以在代码设计方面做得更好吗?
4个回答

8

这是一个非常常见的解决方案。Boost库也采用了这种方式,我也是使用detail名称空间来实现的。只需制定一条规则:“不要查看detail内部内容!”

从文件角度考虑,我建议将详细信息放在自己的文件中,并将其藏在detail文件夹中。也就是说,我的代码类似于:

//           v
#include "detail/RefractObject.hpp"

namespace algorithm {

   template <typename T, typename Y>
   void HandleCollision(T* object1, Y* object2) {
      detail::RefractObject(object1);
      // Another stuff
   }
}

这是一般的良好代码实践(保持事物分离和可重用性),并使头文件不受实现细节的影响。


4
创建一个“静态类” - 不要使用命名空间,而是声明一个同名的类。将构造函数、析构函数、拷贝和赋值运算符设置为私有,然后将每个独立的函数设置为静态成员函数。
一个模板示例:
template<class T>
class StringOperator
{
friend SomeOtherLibraryClass;  // Let it use "Hidden()"
private:
    StringOperator() {}
    ~StringOperator() {}
    StringOperator(const StringOperator&);
    StringOperator& operator=(const StringOperator&);

    static bool  Hidden(const T& input) {
        // Hidden routine end-users shouldn't see...
    }

public:
    static void YourFunction(T& parameter) {
       // Some public code....
    }

    static T     AnotherRoutine(const T* ptr) {
       // Some public code...
    }
};

以下是相对于命名空间的一些优点:
1)您可以将整个类模板化,确保每种类型都有每个函数的版本-您甚至可以根据类型进行特殊化以添加/删除函数。
2)您有一个私有区域,可以隐藏非公共对象和方法的声明
3)通过“友元”机制,您可以让其他对象(例如“SomeOtherLibraryClass”)使用您的函数,而不会将它们暴露给最终用户。

最终用户可以使用“StringOperator :: FunctionName()”访问函数,或者您可以将函数模板化以提供“StringOperator :: FunctionName()”。后者与在命名空间中访问函数的模式相同。


作为附注,与其将规则 5 个成员设置为私有的,不如将它们删除。这更接近您的意图,而且以这种方式甚至无法在类的静态成员内调用它们。 - ljleb

1

除非您首先进行编译,否则无法将源代码隐藏在用户之外,而使用模板是不可能的。因此,在某种意义上,我建议您不要费心。

另外,我想问为什么Refract不能成为成员方法。


成员属于什么?另外,自由函数无论如何都更受欢迎。 :) - GManNickG
我在那里说这只是样例。在我的真实项目中,“算法”包括近似算法和路径查找算法,所以它们不可能成为“成员”(按照那个词的意思)。 - M. Williams
自从Intellisense的发明以来,成员函数比自由函数更受欢迎。 - Puppy
绝对不是这样的。自由函数可以增加封装性和提高可重用性(仅举几个例子),在现代C++设计中,它们是首选选项。函数应该命名得足够好并且足够一致,不需要依赖Intellisense。 :) - GManNickG
它们应该是,但事实并非如此,而Intellisense存在且是如此伟大的工具证明了这一点。为了实际能够使用所需的函数,很高兴减少封装和可重用性。STL在将自由函数仅转储到std中方面特别糟糕,因此无法在没有引用的情况下使用。 - Puppy

0

我曾经也考虑过同样的问题。最终,我决定使用类,以便代码更具可移植性、可重用性和通用性。

  • 类允许进行各种元编程,如特征、标签分派和策略。
  • 类已经有了公共、私有和受保护的成员。

你会使用命名空间的唯一两个原因:

  • 解决命名冲突 std::for_each vs boost::for_each
  • 减少视觉噪音。例如,using namespace boost::filesystem,可以让你直接调用其函数而不需要::

即使对于这两种情况,你也可以继承具有方法的类或通过初始化重命名(my_own_filesystem_methods fs;),并将其用作fs.mkdir()。此外,.::更短。

命名空间还有其他好处,如std::cout << "hi" << endl;,但这并不是非常重要。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接