一个C++有关构造函数的问题,没想明白

2019-03-25 13:23发布

本帖最后由 wsmysyn 于 2018-10-26 21:07 编辑

如题,C++高级特性不怎么经常用,一直当C来用,偶尔调用别人的C++ 类的API,
今天遇到一个没想明白的问题,如图。

我写了三个类CA、CB、CC
构造函数部分,
CA采用无参构造
CB采用了一个void类型的指针参数,默认为NULL
CC采用一个int类型的指针参数,默认为NULL

然后在Test函数中进行实例化
使用new和不使用new的区别是,new在堆上创建对象,没有new是在栈上创建对象
new创建的对象,可以全局访问,不需要时,需要用delete;栈上的对象,在离开作用域之后,自动销毁

使用new的时候,需要一个类的指针,来指向这个对象

我分别用三种语法来写,
Cx cx;   --> 1

Cx cx = new Cx();  --> 2

Cx* cx = new Cx(); --> 3


发现只有CB的类是可以支持这三种写法,其他都是不支持2的写法,提示没有相应的构造函数,是Cx*转换为Cx
这个我理解,new的实现是返回一个void类型的指针,和左值类型不符

我的问题:
1、为什么构造函数传入void类型指针之后就可以支持2这种操作呢?
2、从运行的结果上看,2这种实例化方式,调用了两次CB的构造函数,这是为什么呢?
3、在函数结尾,delete三个指针,销毁了这三个对象,分别调用了三个类的析构函数,以及函数结束后,栈上的对象也被销毁,同样分别调用了三个类的析构函数。但是为什么少了1次CB的析构函数呢?构造的时候调用了4次,为什么析构的时候,只有3次呢?

浏览了一下C++ 11的标准,但是并没有找到地方
网上暂时也没搜到这类的描述,(可能提问的姿势不对...)


代码:

TIM截图20181026203531.png
运行结果:

TIM截图20181026203937.png

  1. class CA
  2. {
  3. public:
  4.     CA() { std::cout << "Class A" << std::endl; };
  5.     ~CA() { std::cout << "Class A die..." << std::endl; };
  6. };
  7. class CB
  8. {
  9. public:
  10.     CB(void* pVOID = NULL) { std::cout << "Class B" << std::endl; };
  11.     ~CB() { std::cout << "Class B die..." << std::endl; };
  12. };
  13. class CC
  14. {
  15. public:
  16.     CC(int* pLength = NULL) { std::cout << "Class C" << std::endl; }
  17.     ~CC() { std::cout << "Class C die..." << std::endl; };
  18. };

  19. void Test(void)
  20. {
  21.     CA  a0;
  22.     //CA  a1 = new CA();
  23.     CA* a2 = new CA();

  24.     CB  b0;
  25.     CB  b1 = new CB();
  26.     CB* b2 = new CB();

  27.     CC  c0;
  28.     //CC  c1 = new CC();
  29.     CC* c2 = new CC();

  30.     delete a2;
  31.     delete b2;
  32.     delete c2;
  33. }

  34. int main(int argc, char* argv[])
  35. {   
  36.     Test();
  37.     system("pause");

  38.     return 0;
  39. }
复制代码



此帖出自小平头技术问答
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
1条回答
wsmysyn
2019-03-25 17:01
< 后来搞明白了。。
实际上是C++的一种隐式转换

CB b1 = new CB();

等同于 -->
CB* temp = new CB();
CB b1(temp);

因为CB的构造函数传入类型是void*,new 返回的也是void*类型,所以默认尝试做了一个隐式转换,将临时对象,作为构造函数的参数,传递进去了。并在栈上做了创建了一个对象,所以可以通过编译(可能并不是你想要的这种结果,但却是发生了)

所以,构造函数是4次,多了一次隐式的对象构造;在析构的时候,临时对象并没有被析构掉,造成了内存泄露

尤其是单参构造函数出现隐式转换的几率比较大,可能会有意想不到的bug。避免这种隐式的转换是在构造函数前加上explicit 禁止隐式转换。

一周热门 更多>