• C++ 前置声明错误使用导致的内存泄漏及正确使用方式


    前置声明使用注意:

    1. 只能定义指向前置声明的类的指针或引用。
    2. 以前置声明类型为函数的参数或返回值,该函数只能声明不能定义。

    1. 前置声明误用导致内存泄漏

    看下面的代码:

    #include 
    class A;
    void del(A*a){
    	delete a;
    }
    
    class A{
    public:
    	A(){
    		printf("abc/r/n");
    	}
    	~A(){
    		printf("~abc/r/n");
    	}
    };
    
    int main()
    {
    	A* a= new A;
    	del(A);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    编译和运行都不报错。

    运行结果:

    A()
    
    • 1

    但是并没有调用A的析构函数,为什么?违反了第二点以前置声明类型为函数的参数或返回值,该函数只能声明不能定义

    编译时有警告:

    : In function ‘void del(A*)’:

    :4:9: warning: possible problem detected in invocation of ‘operator delete’ [-Wdelete-incomplete]

        4 | delete a;

            | ^~~~~~~~

    :3:13: warning: ‘a’ has incomplete type

        3 | void del(A* a){

            |             ~~~^

    提示’a’是一个不完整类型。如果不认真看警告,真不容易发现。程序不会有错误,但是会有内存泄漏。

    为什么会这样?
    因为 C++ 编译器自上而下编译源文件,del(A* a)中a是指针,已经知道占据内存的大小,所以可以编译成功。
    若在del中sizeof(A)获取A的大小则会提示“A是不完整类型”的错误而编译失败:

    void del(A* a){
        printf("a size %d\r\n",sizeof(A));
    	delete a;
    }
    
    • 1
    • 2
    • 3
    • 4

    若改成以下这样,是否可以:

    #include 
    class A;
    void del(A*a);
    
    class A{
    public:
    	A(){
    		printf("abc/r/n");
    	}
    	~A(){
    		printf("~abc/r/n");
    	}
    };
    
    void del(A*a){
    	delete a;
    }
    
    int main()
    {
    	A* a= new A;
    	del(A);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    当然是没问题的。

    2. 前置声明常见使用方法

    若两个类互相引用,则需要前置声明。
    下面的代码是否有问题?

    class A;
    
    class B{
    public:
        A a;
    };
    
    class A{
    public:
        B b;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    毫无疑问是有问题的,A和B初始化时递归初始化成员,进入死循环。再一个,class B中A a是不完整类型,违反了第一点只能定义指向前置声明的类的指针或引用。不能直接定义类成员,而应该定义成指针:

    class A;
    
    class B{
    public:
        A* a;
    };
    
    class A{
    public:
        B* b;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    这样就没问题了,虽然class B中定义了A *a,A是不完整类型,但是指针的大小是确定的,并不影响B的定义。

    思考一下,class A改成下面这样可以吗?

    class A{
    public:
        B b;
    };
    
    • 1
    • 2
    • 3
    • 4

    当然是可以的,因为B在A前已经完整定义了。

  • 相关阅读:
    Hive安装与配置实战指南
    课堂练习12 继承与多态
    Python Selenium 浏览器打印预览
    lv7 嵌入式开发-网络编程开发 10 TCP协议是如何实现可靠传输的
    【数据结构--八大排序】之希尔排序
    Spark中常用的聚合算子说明及使用
    vue 使用webpack打包,出现路径404 ,导致白屏&webpack-bundle-analyzer使用
    安卓高通机型的基带移植 修改 编译的相关 增加信号 支持5G等
    使用Ubuntu系统搭建RabbitMQ服务并结合内网穿透实现公网访问
    TiDB 6.1 部署本地测试集群(支持 macOS 和 Linux)
  • 原文地址:https://blog.csdn.net/lwwl12/article/details/133772649