C++中空类的优势

22

拥有一个空类可能会有哪些优点或用途?

附言: 这个问题对于一些人来说可能听起来很琐碎,但它只是为了学习目的,没有实际意义。值得注意的是,谷歌搜索并没有提供帮助。


有一些情况下这可能很有用;你能给一个例子吗,这样我们就可以有一些上下文了吗? - Nate
1
是指没有成员或函数的空,还是使用了Null Object模式的空? - SwDevMan81
1
你看到这个链接了吗?http://www.cplusplus.com/forum/general/8370/ - bjskishore123
空的,没有成员或函数。 - Ron
请记住,如果您创建一个没有成员或函数的类,它仍然需要至少一个字节的内存空间。 - David Thornley
显示剩余3条评论
10个回答

21

其中一种用途是在模板(元)编程中:例如,迭代器标签被实现为空类。这里唯一的目的是在编译时传递信息,以便您可以检查传递给例如模板函数的迭代器是否满足特定要求。

示例:

这只是为了让您有一个想法而进行的非常简化。 这里标签类的目的是决定要使用哪个算法实现:

class forward_iterator_tag {};
class random_access_iterator_tag {};

class MySimpleForwardIterator {
public:
  typedef typename forward_iterator_tag tag;
  // ...
};

class MySimpleRandomIterator {
public:
  typedef typename random_access_iterator_tag tag;
  // ...
};

template<class iterator, class tag>
void myfunc_int(iterator it, tag t) {
  // general implementation of myfunc
}

template<class iterator>
void myfunc_int<iterator, forward_iterator_tag>(iterator it) {
  // Implementation for forward iterators
}

template<class iterator>
void myfunc_int<iterator, random_access_iterator_tag>(iterator it) {
  // Implementation for random access iterators
}

template<class iterator>
void myfunc(iterator it) {
  myfunc_int<iterator, typename iterator::tag>(it);
}

使用这段代码,您可以在任意迭代器上调用myfunc函数,并让编译器根据迭代器类型(即标签)选择正确的实现方式。

1
查看一些STL迭代器的实现,你应该在任何编译器中都有代码。 - Stephane Rolland
这个习惯用法被称为 标签派发 - Maxim Egorushkin

5
以下代码可以用于创建一个能够包含(SQL)NULL值的boost::variant变量。
class Null { };

typedef boost::variant<Null, std::string, int> Value;

为了使像operator==operator<<这样的东西更加有用。例如:
std::ostream& operator<<(std::ostream &lhs, const Null &rhs)
{
     lhs << "*NULL*";
     return lhs;
}

int main()
{
    Variant v("hello");
    std::cout << v << std::endl;
    v = Null();
    std::cout << v << std::endl;
    ...
}

Will give:

hello
*NULL*

3
在C++的STL(标准模板库)中,例如你有:
template<class _Arg,
 class _Result>
struct unary_function
    { // base class for unary functions
 typedef _Arg argument_type;
 typedef _Result result_type;
    };

在定义一个函数对象时,您可以继承unary_function,然后您就可以自动使用定义的typedef。


2
一个空类可以被用作“令牌”,定义某个独特的东西;在某些模式中,您需要一个不依赖于实现的表示唯一实例的方式,该方式对开发人员没有其他价值,除了其唯一性。其中一个例子是工作单元(Unit of Work);您可能不关心执行者内部正在发生的任何事情,但是您想要告诉该执行者,您要求它执行的任务是原子集的一部分。在这种情况下,代表工作单元对外界的空类可能是完美的;一个工作单元对象可以存储或执行的几乎所有内容(封装DB事务,公开提交/回滚行为),都会开始将您与特定的实现绑定在一起,但是对象引用有助于提供一个唯一但可复制和可传递的任务原子集引用。

2

您可以将其用作检查目的的占位符,或者作为特殊功能的启用程序。例如,在Java中存在“空”接口Serializable,用于指定类是否可序列化。


0
如其他人所说,通常会使用空类(或结构体)作为占位符、区分器、标记等。
例如,很多人不知道有"nothrow"版本的operator new。调用nothrow new的语法是:
p = new(std::nothrow) Bar;

而std::nothrow被简单地定义为

struct nothrow_t {}; //defined in namespace std

0
MartinStettner 的回答很好,但这里要强调一个重要的观点:C++ 中迭代器标记或任何标记的概念并非严格依赖于空类。如果 STL 编写者想要,C++ 标记完全可以是非空类;虽然这样也可以工作,但对于通常保留的编译时特技而言,并不会增加任何附加值。

0
为了 typeid 的缘故
假设我们有一个可比较的接口 Id。我们需要用这个接口的唯一实例来填充一些容器。如何保证由独立软件部分产生的 Id 实例的唯一性?“独立部分”意味着不同的动态库,由不同的程序员从不同的地方编译。
其中一个决策是先比较某个类型的 typeid。如果 typeid 匹配,则转换并比较其他实现特定属性。C++ 语言保证任何类型在进程内存中都是唯一的。应该使用哪种类型来实现这个目的?任何资源消耗最小的类型——空类型。

0
“空”类是指没有数据成员的类吗? 它们通常声明typedef或成员函数,并且您可以使用自己的类扩展它们。

伪代码 类Shape { 虚拟 double area(); }类Circle扩展Shape { double area() }类Square扩展Shape { double area() } - Javi Moya
1
您可以通过选择“编辑”按钮来编辑您的答案,并将代码示例放在那里,而不是在评论中。这样会使其更易读。 - SwDevMan81

0

这里有一个有趣的链接,其中包含为什么允许使用的答案。你可能会发现这对于寻找可能有用的情况很有帮助。


你提到的文章谈论了没有数据成员的类。只有函数成员的类被广泛使用(例如用于接口定义)。我认为OP指的是没有任何成员的空类。 - MartinStettner
@MartinStettner - 我在这个问题中的帖子是关于为什么允许这样做的。我发布的另一篇文章是为了展示Javi的答案来自哪里。阅读文章,你可以在文章中逐字看到答案。我并不是把它作为一个答案发布的。 - SwDevMan81

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