前置声明使用注意:
- 只能定义指向前置声明的类的指针或引用。
- 以前置声明类型为函数的参数或返回值,该函数只能声明不能定义。
1. 前置声明误用导致内存泄漏
看下面的代码:
#include <stdio.h>
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);
}
编译和运行都不报错。
运行结果:
A()
但是并没有调用A的析构函数,为什么?违反了第二点以前置声明类型为函数的参数或返回值,该函数只能声明不能定义。
编译时有警告:
<source>: In function ‘void del(A*)’:
<source>:4:9: warning: possible problem detected in invocation of ‘operator delete’ [-Wdelete-incomplete]
4 | delete a;
| ^~~~~~~~
<source>: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;
}
若改成以下这样,是否可以:
#include <stdio.h>
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);
}
当然是没问题的。
2. 前置声明常见使用方法
若两个类互相引用,则需要前置声明。
下面的代码是否有问题?
class A;
class B{
public:
A a;
};
class A{
public:
B b;
};
毫无疑问是有问题的,A和B初始化时递归初始化成员,进入死循环。再一个,class B中A a是不完整类型,违反了第一点只能定义指向前置声明的类的指针或引用。不能直接定义类成员,而应该定义成指针:
class A;
class B{
public:
A* a;
};
class A{
public:
B* b;
};
这样就没问题了,虽然class B中定义了A *a,A是不完整类型,但是指针的大小是确定的,并不影响B的定义。
思考一下,class A改成下面这样可以吗?
class A{
public:
B b;
};
当然是可以的,因为B在A前已经完整定义了。