注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

C++和C中的不完整类型定义  

2015-10-14 17:24:17|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
C++和C中的不完整类型定义
在调用一些库的函数时,特别是C++中,其对类(结构体)要求是完整定义的,那么什么是完整类型定义什么是不完整类型定义呢?
举一个简单的例子:
在老版本(98/03)
class Widget {       // still in header "widget.h"
public:
    Widget();
    ~Widget();         // dtor is needed—see below
    …
private:
    struct Impl;        // declare implementation struct
    Impl *pImpl;       // and pointer to it
};
CPP文件: 
struct Widget::Impl {               // definition of Widget::Impl
    std::string name;                // with data members formerly
    std::vector<double> data;  // in Widget
    Gadget g1, g2, g3;
};


Widget::Widget()   // allocate data members for
: pImpl(new Impl)  // this Widget object
{}


Widget::~Widget() // destroy data members for
{ delete pImpl; }     // this object

这个程序是OK的,但是如果使用C++11以上:

class Widget {                              // in "widget.h"
public:
    Widget();
    …
private:

    struct Impl;
    std::unique_ptr<Impl> pImpl;   // use smart pointer
};    
CPP文件: 
struct Widget::Impl {            // as before
    std::string name;
    std::vector<double> data;
    Gadget g1, g2, g3;
};


Widget::Widget()                             // per Item 21, create
: pImpl(std::make_unique<Impl>()) // std::unique_ptr
{}   
这里看到没有了显示的析构函数。
由于使用了智能指针,所以理论上说,Widget的析构就可以让编译器自动生成了。但实际上,这个可能就会报错。也就是说:这段代码可以通过编译,但很可惜在少数的客户端不能使用。原因就是上面的“不完整类型定义”,因为std::unique_ptr在引用为0时,自动销毁
对象,在删除前,c++11中典型的实现会使用static_assert去确保原始指针没有指向不完整类型,当编译器为Widget w的析构函数产生代码时,会遇到一个static_assert失败,于是就导致了错误发生。这个错误产生在w被销毁处,因为Widget的析构函数和其他的编译器产生的特殊的成员函数一样,都是内联函数。这个编译错误通常会指向w生成的代码行,因为正是创建对象的这行源代码导致了隐式析构
为了修复这个问题,你只需要保证在生成析构std::unique<Widget::Impl>代码的地方,Widget::Impl是个完整类型。当类型的定义可以被看到时,类型就是完整的。而Widget::Impl是定义在Widget.cpp文件中的。

成功编译的关键是让编译器在widget.cpp在Widget::Impl定义之后看到Widget的析构函数的函数体。
这个很简单,在widget.h中声明Widget的析构函数,但是不在那里定义它:

class Widget {                    // as before, in "widget.h"
public:
    Widget();
    ~Widget();                      // declaration only
    …
private:                              // as before
    struct Impl;
    std::unique_ptr<Impl> pImpl;
};



在widget.cpp中,Impl的定义之后再定义该析构函数:


// as before, in "widget.cpp"
struct Widget::Impl {         // as before, definition of

std::string name;              // Widget::Impl
std::vector<double> data;
Gadget g1, g2, g3;
};


Widget::Widget()                                // as before
: pImpl(std::make_unique<Impl>())
{}
Widget::~Widget()                               // ~Widget definition
{}
以上参考:http://blog.csdn.net/coolmeme/article/details/43481797
那么回到问题上来,到底什么是不完整类型定义呢?简单说来,只声明不定义就是不完整类型,又或者你有声明有定义但是编译器由于某种原因看不到,也算是不完整类型定义。就如上面一样,编译器就认为你没完整定义。换句话说,完整不完整,得编译器说了算,它得看见。
int main ()  
{  
    /* 定义不完整结构类型 */  
    struct user;  
    /* 可以定义指向不完整结构类型的指针 */  
    struct user *p;  
    /* 补充结构定义 */  
    struct user {  
        char *name;  
        int age;  
    };  
    /* 只有完整的结构类型可以定义变量 */  
    struct user qifei;  
    qifei.name = "qifei";  
    qifei.age = 28;  
    p = &qifei;  
    printf("user name:%s/n", p->name);  
    printf("user age :%d/n", p->age);  
    return 0;  
这就明白了吧,不过,在Pimpl(指向实现的指针)中一定要注意小心不完整类型定义。
特别说明一下,在封装C++11库的std::condition_variable (StdWait)时,也是遇到析构函数挂掉,当时也是用的PIMPL,会不会也是这个原因,不敢太肯定。
  评论这张
 
阅读(269)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017