Chinaunix首页 | 论坛 | 博客
  • 博客访问: 198371
  • 博文数量: 264
  • 博客积分: 6010
  • 博客等级: 准将
  • 技术积分: 2740
  • 用 户 组: 普通用户
  • 注册时间: 2009-06-03 13:25
文章分类

全部博文(264)

文章存档

2011年(1)

2009年(263)

我的朋友

分类: C/C++

2009-06-09 15:04:12

这段时间整理旧资料,看到一些文章,虽然讲的都是些小问题,不大可能用到,但也算是一个知识点,特整理出来与大家共享。与此相关的那篇文章的作者的有些理解是错误的,我写此文,也是纠正为了作者的一些错误认识。当然,如果我的理解有任何错误,也恳请大家批评指正。

  C++虽说被B.S.称作一门新语言,但它毕竟与C有着千丝万缕的联系,虽然B.S.一再坚持,但我还是愿意把C++看作是C ++。

  我们应该按照C中的convention去使用union,这是我这篇文章要给出的观点。虽然C++使得我们可以扩展一些新的东西进去,但是,我建议你不要那样去做,看完这篇文章之后,我想你大概也是这么想的。

  C由于没有类的概念,所有类型其实都可以看作是基本类型的组合,因此在union中包含struct也就是一件很自然的事情了,到了C++之后,既然普遍认为C++中的struct与class基本等价,那么union中是否可以有类成员呢?先来看看如下的代码:

  struct TestUnion
  {
  TestUnion() {}
  };

  typedef union
  {
  TestUnion obj;
  } UT;

  int main (void)
  {
  return 0;
  }

  编译该程序,我们将被告知:

error C2620: union ‘__unnamed‘ : member ‘obj‘ has user-defined constructor or non-trivial default constructor


  而如果去掉那个什么也没干的构造函数,则一切OK。

  为什么编译器不允许我们的union成员有构造函数呢?我无法找到关于这个问题的比较权威的解释,对这个问题,我的解释是:

  如果C++标准允许我们的union有构造函数,那么,在进行空间分配的时候要不要执行这个构造函数呢?如果答案是yes,那么如果TestUnion的构造函数中包含了 一些内存分配操作,或者其它对整个application状态的修改,那么,如果我今后要用到obj的话,事情可能还比较合理,但是如果我根本就不使用 obj这个成员呢?由于obj的引入造成的对系统状态的修改显然是不合理的;反之,如果答案是no,那么一旦我们今后选中了obj来进行操作,则所有信息 都没有初始化(如果是普通的struct,没什么问题,但是,如果有虚函数呢?)。更进一步,假设现在我们的union不是只有一个TestUnion obj,还有一个TestUnion2 obj2,二者均有构造函数,并且都在构造函数中执行了一些内存分配的工作(甚至干了很多其它事情),那么,如果先构造obj,后构造obj2,则执行的 结果几乎可以肯定会造成内存的泄漏。

  鉴于以上诸多麻烦(可能还有更多麻烦),在构造union时,编译器只负责分配空间,而不负责去执行附加的初始化工作,为了简化工作,只要我们提供了构造函数,就会收到上面的error。

  同理,除了不能加构造函数,析构函数/拷贝构造函数/赋值运算符也是不可以加。

  此外,如果我们的类中包含了任何virtual函数,编译时,我们将收到如下的错误信息:

error C2621: union ‘__unnamed‘ : member ‘obj‘ has copy constructor

  所以,打消在union中包含有构造函数/析构函数/拷贝构造函数/赋值运算符/虚函数的类成员变量的念头,老老实实用你的C风格struct吧!
  不过,定义普通的成员函数是OK的,因为这不会使得class与C风格的struct有任何本质区别,你完全可以将这样的class理解为一个C风格的struct + n个全局函数。 现在,再看看在类中包含内部union时会有什么不同。看看下面的程序,并请注意阅读程序提示:

  class TestUnion
  {
  union DataUnion
  {
  DataUnion(const char*);
  DataUnion(long);
  const char* ch_;
  long l_;
  } data_;

  public:
  TestUnion(const char* ch);
  TestUnion(long l);
  };

  TestUnion::TestUnion(const char* ch) : data_(ch) // if you want to use initialzing list to initiate a nested-union member, the union must not be anonymous and must have a constructor。
  {
  }

  TestUnion::TestUnion(long l) : data_(l)
  {
  }

  TestUnion::DataUnion::DataUnion(const char* ch) : ch_(ch)
  {
  }

  TestUnion::DataUnion::DataUnion(long l) : l_(l)
  {
  }

  int main (void)
  {
  return 0;
  }

正如上面程序所示,中的union也可以包含构造函数,但是,这虽然被语言所支持,但实在是一种不佳的编程习惯,因此,我不打算对上面的程序进行过多的说明。我更推荐如下的编程风格:

  class TestUnion
  {
  union DataUnion
  {
  const char* ch_;
  long l_;
  } data_;
  
  public:
  TestUnion(const char* ch);
  TestUnion(long l);
  };

  TestUnion::TestUnion(const char* ch)
  {
  data_。ch_ = ch;
  }

  TestUnion::TestUnion(long l)
  {
  data_。l_ = l;
  }

  int main (void)
  {
  return 0;
  }

  它完全是C风格的。

阅读(244) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~