V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
XuanFei990
V2EX  ›  C

问一个简单的 C++关于构造函数和 new 表达式的问题

  •  
  •   XuanFei990 · 2018-10-27 18:36:26 +08:00 · 2210 次点击
    这是一个创建于 1979 天前的主题,其中的信息可能已经有所发展或是发生改变。

    1、为什么 CB 类可以使用 CB b1 = new CB();的方式,CA 和 CC 提示类型不符,这个从 new 的返回类型是 void*可以知道原因; CB 可以这么写仅仅因为构造函数传递的是 void* ??就可以从 CB*转换为 CB 类型么?

    2、为什么 b1 会调用两次 CB 的构造函数??从生成的汇编代码上确实看到,调用了两次构造;但是在 Test 函数结束后,只是少调用了一次 CB 析构函数,前边一共有 4 次 CB 的构造函数,在 Test 函数结束后,只看到了 3 次 CB 的析构函数这是为什么?使用 delete b1 是语法错误,因为 b1 不是指针类型。

    环境
    VS2017 + C++14 语言的设置

    class CA
    {
    public:
        CA() { std::cout << "Class A" << std::endl; }; //无参构造函数
        ~CA() { std::cout << "Class A die..." << std::endl; };
    };
    class CB
    {
    public:
        CB(void* pVOID = NULL) { std::cout << "Class B" << std::endl; }; //构造需要传递指向 void 类型的指针,默认为 NULL
        ~CB() { std::cout << "Class B die..." << std::endl; };
    };
    class CC
    {
    public:
        CC(int* pLength = NULL) { std::cout << "Class C" << std::endl; } //构造需要传递指向 int 类型的指针,默认为 NULL
        ~CC() { std::cout << "Class C die..." << std::endl; };
    };
    
    void Test(void)
    {
        CA  a0;
        //CA  a1 = new CA();  //缺少合适构造函数,使 CA* 转换为 CA
        CA* a2 = new CA();
    
        CB  b0;
        CB  b1 = new CB(); //为什么这里就可以这么使用呢?并且从运行结果上看执行了两次 CB 的构造函数?
        CB* b2 = new CB();
    
        CC  c0;
        //CC  c1 = new CC(); //缺少合适构造函数,使 CC*转换为 CC
        CC* c2 = new CC();
    
        delete a2;
        //delete b1; //不是 CB*类型
        delete b2;
        delete c2;
    }
    
    8 条回复    2018-10-27 23:11:04 +08:00
    wevsty
        1
    wevsty  
       2018-10-27 19:18:19 +08:00
    new CB();调用了第一次构造函数返回的类型是 CB*,所有的指针都可以转换为 void*类型,所以编译器帮你使用了 CB 的构造函数,把 new CB()得到的指针作为参数传递进去了,所以 b1 这个对象也不是 new 出来的,你当然也不能对他 delete。这个是隐性的数据类型转换导致的结果。
    XuanFei990
        2
    XuanFei990  
    OP
       2018-10-27 19:50:34 +08:00
    @wevsty 1、调用了两次构造函数,是构造了两个完整的对象么?一次在堆中构造,一次在栈中构造?
    2、作为参数传递进去,意思是在 CB 的构造函数中可以通过 pVOID 来访问到第一次构造的对象?

    最近在看一个别的公司提供的一个操作 USB Device 的 API 源码,,构造函数就是形如这个的,只不过是用的 HANDLE,实际上也是通过 typedef 把 void*起了个别名,在测试的时候,发现这么写居然也可以通过编译

    实际应用中,CB b1 = new CB();这种写法有什么后遗症么??

    正常应该使用 CB* b1 = new CB();这种方式对么?
    nicebird
        3
    nicebird  
       2018-10-27 20:21:32 +08:00
    CB b1 = new CB();
    等价于
    CB *temp = new CB();
    CB b4(temp);

    1. 发送了一次隐式转换,然后构造,为了防止这种现象使用 explicit 关键字。
    2. temp 没有被释放掉。产生了内存泄漏。
    justou
        4
    justou  
       2018-10-27 20:28:42 +08:00
    不该隐式转换的都尽量避免, 构造器加 explicit 修饰, 尤其是单参数的构造器.
    后遗症就是容易出现这种隐式转换导致的各种奇怪 bug
    innoink
        5
    innoink  
       2018-10-27 20:42:37 +08:00
    你只要搞明白,为什么第一个 new CB 内存泄漏了,就全明白了
    XuanFei990
        6
    XuanFei990  
    OP
       2018-10-27 23:00:33 +08:00
    @justou 哦,这样,那明白了,,那就是这个 API,实际上是有潜在 bug,最好应该加上 explicit ?防止这种隐式转换是么?
    XuanFei990
        7
    XuanFei990  
    OP
       2018-10-27 23:01:59 +08:00
    @nicebird 嗯,明白了,刚开始看高级特性,知道遇到的少了些。。。以前都是当 C 用的。。。
    f4nyc
        8
    f4nyc  
       2018-10-27 23:11:04 +08:00 via iPhone
    不是 ub 吗……
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   981 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 22:08 · PVG 06:08 · LAX 15:08 · JFK 18:08
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.