检查类型是否为不完整(不完全)类型

时间:2022-11-10 20:58:00

概念


incomplete types (types that describe objects but lack information needed to determine their sizes).      

也就是这样一些对象:编译器知道对象的类型,但是缺乏另一些相关信息使其大小能够确定.

列举一些举例,来自IBM的 langue reference (地址)

比较简单,直接上原文了:

The following are incomplete types:

  • Type void
  • Array of unknown size
  • Arrays of elements that are of incomplete type
  • Structure, union, or enumerations that have no definition
  • 检查类型是否为不完整(不完全)类型 Pointers to class types that are declared but not defined
  • 检查类型是否为不完整(不完全)类型 Classes that are declared but not defined

void is an incomplete type that cannot be completed. Incomplete structure or union and enumeration tags must be completed before being used to declare an object, although you can define a pointer to an incomplete structure or union.

检查类型是否为不完整(不完全)类型 An array with an unspecified size is an incomplete type. However, if, instead of a constant expression, the array size is specified by [*], indicating a variable length array, the size is considered as having been specified, and the array type is then considered a complete type.

检查类型是否为不完整(不完全)类型 If the function declarator is not part of a definition of that function, parameters may have incomplete type. The parameters may also have variable length array type, indicated by the [*] notation.

The following examples illustrate incomplete types:

void *incomplete_ptr; struct dimension linear; /* no previous definition of dimension */ void

is an incomplete type that cannot be completed. Incomplete structure, union, or enumeration tags must be completed before being used to declare an object. However, you can define a pointer to an incomplete structure or union.

 

一般情况下,也不会有什么大问题,因为既然对象的大小不能确定,那么这个对象自然不能创建(new),因为编译器不知道它的大小,也不能释放,因为对象没有创建成功.

但是,应用场景往往都是复杂的,比如使用前置声明,就会有问题.下面模拟下这样的情况:

   1: #include <iostream>
   2: class proc;
   3: class killer
   4: {
   5: public:
   6:     void kill( proc* to_kill)
   7:     {
   8:         delete to_kill;
   9:     }
  10: };
  11: class proc
  12: {
  13: public:
  14:     proc(){std::cout <<"proc - ctor" <<std::endl;};
  15:     ~proc(){std::cout <<"proc - dtor" <<std::endl;};
  16: };
  17: void    do_test()
  18: {
  19:     proc* proc_ptr=new proc();
  20:     killer proc_consol;
  21:     proc_consol.kill(proc_ptr);
  22: }
  23: int _tmain(int argc, _TCHAR* argv[])
  24: {
  25:     do_test();
  26:     return 0;
  27: }

编译结果:

warning C4150: 删除指向不完整“proc”类型的指针;没有调用析构函数

程序输出:

proc - ctor

明显 delete to_kill这句只是打了个酱油,没按照我们的预期做事,因为编译器不知道proc类型到底是个什么东西,更不知到其析构函数的定义.

假设把上面的代码,调整一下,把类proc的定义放到类killer的前面去,情况就不同了程序的输出为:

proc - ctor
proc - dtor

这下正确了,因为我们意识到有代码有问题,去解决了,那如果我们疏忽了,或者在更加复杂的环境,怎么办?编译器并不能帮我们解决这个后顾之忧,得靠程序员自己编写一些guard语句之类来防止这种错误.一般情况下是这样做的,在适当的地方写上这样一句:

typedef char type_must_be_complete[sizeof(T)];

 

现在,原来的例子就应该是这个样子:

   1: #include <iostream>
   2: class proc;
   3: class killer
   4: {
   5: public:
   6:     void kill( proc* to_kill)
   7:     {
   8:         typedef char type_must_be_complete[sizeof(proc)];
   9:         delete to_kill;
  10:     }
  11: };
  12:  
  13: class proc
  14: {
  15: public:
  16:     proc(){std::cout <<"proc - ctor" <<std::endl;};
  17:     ~proc(){std::cout <<"proc - dtor" <<std::endl;};
  18: };
  19:  
  20: void    do_test()
  21: {
  22:     proc* proc_ptr=new proc();
  23:     killer proc_consol;
  24:     proc_consol.kill(proc_ptr);
  25: }
  26: int _tmain(int argc, _TCHAR* argv[])
  27: {
  28:     do_test();
  29:     return 0;
  30: }

这样的话,这段代码根本就编译不过 :),会有类似这样的错误信息:

error C2466: 不能分配常量大小为 0 的数组
error C2027: 使用了未定义类型“proc”

着实让人觉得安稳了些,同时也说明C++对新手确实不太友善,不知道要交多少学费,才能”熟练”使用……