所谓「前置声明」(forward declaration)是类、函数和模板的纯粹声明,没伴随着其定义.
#include
using namespace std;
void fun(char ch, int *pValue, double dValue);
void main()
{
int nValue = 100;
double dValue = 111.22;
fun('a', &nValue, dValue);
system("pause");
}
void fun(char ch, int *pValue, double dValue)
{
return;
}
声明一个将稍后在此作用域定义的类类型。直到定义出现前,此类名具有不完整类型
。这些类之间允许彼此引用:
class Vector; // 前置声明
class Matrix {
// ...
friend Vector operator*(const Matrix&, const Vector&);
};
class Vector {
// ...
friend Vector operator*(const Matrix&, const Vector&);
};
自定义类型的前置声明,由于编译器不知道类型的大小,所以不可以声明类型的对象。只可以利用类型声明指针和引用:
class B;
int main()
{
B b; // error: aggregate 'B b' has incomplete type and cannot be defined
B b1 = new B; // error: variable 'B b1' has initializer but incomplete type
return 0;
}
#include
using namespace std;
class B;
class A
{
private:
// 内置类型
int m_nInt;
int& m_nRInt;
int* m_pInt;
// 自定义类型
// B b; // error!
B* m_pB;
B& m_b;
public:
A (B *pBPara = NULL) : m_nInt(100)
, m_nRInt(m_nInt)
, m_pInt(NULL)
, m_pB(NULL)
, m_b((NULL == pBPara) ? (*m_pB) : (*pBPara))
{
cout << "A()" << endl;
}
~A()
{
cout << "~A()" << endl;
}
void funA()
{
// m_pB->doAnything(); // build error C2027: use of undefined type 'B'
}
};
class B
{
private:
int m_n;
public:
B (int n = 100) : m_n(n)
{
cout << "B()" << endl;
}
~B()
{
cout << "~B()" << endl;
}
void doAnything()
{
cout << "B::anythig()" << endl;
}
};
void main()
{
A objA;
system("pause");
}
如上,利用前置类型的指针想调用其成员函数,会报编译错误!那么,肿么办?请看下文。
代码如下,声明头文件:
/*
* TestForwardDeclar.h
*/
#ifndef D_TESTFORWARDDECLAR_H_
#define D_TESTFORWARDDECLAR_H_
#include
class B; // 前置声明自定义类型
class A
{
private:
// 内置类型
int m_nInt;
int& m_nRInt;
int* m_pInt;
// 自定义类型
// B b; // error!
B* m_pB;
B& m_b;
public:
A (B *pBPara = NULL);
~A ();
void funA();
};
class B
{
private:
int m_n;
public:
B (int n = 100);
~B ();
void doAnything();
};
#endif
代码如下,定义文件:
/*
* TestForwardDeclar.cpp
*/
#include "TestForwardDeclar.h"
#include
A::A (B *pBPara)
: m_nInt(100)
, m_nRInt(m_nInt)
, m_pInt(NULL)
, m_pB(NULL)
, m_b((NULL == pBPara) ? (*m_pB) : (*pBPara))
{
std::cout << "A()" << std::endl;
}
A::~A()
{
std::cout << "~A()" << std::endl;
}
void A::funA()
{
m_pB->doAnything(); // 分开头文件和实现文件即可
}
B::B (int n) : m_n(n)
{
std::cout << "B()" << std::endl;
}
B::~B()
{
std::cout << "~B()" << std::endl;
}
void B::doAnything()
{
std::cout << "B::anythig()" << std::endl;
}
代码如下:测试文件:
#include "TestForwardDeclar.h"
void main()
{
A objA;
}
编译成功,运行结果是期望效果。
#include
的依赖:// 在 MyStruct.h 中
#include // 含有 std::ostream 的前置声明
struct MyStruct {
int value;
friend std::ostream& operator<<(std::ostream& os, const S& s);
// 其定义在 MyStruct.cpp 文件中提供,该文件使用 #include
};
如果前置声明出现在局部作用域,则它隐藏其外围作用域中可出现的先前声明的相同名字的类、变量、
函数,以及所有其他声明:
struct s { int a; };
struct s; // 不做任何事(s 已定义于此作用域)
void g() {
struct s; // 新的局部类“s”的前置声明
// 它隐藏全局的 struct s 直至此块结尾
s* p; // 指向局部 struct s 的指针
struct s { char* p; }; // 局部 struct s 的定义
}
注意,通过作为其他声明一部分的详述类型说明符,也可以引入新的类名,但仅当名字查找无法找到先前声明的有此名的类时才行。
class U;
namespace ns{
class Y f(class T p); // 声明函数 ns::f 并声明 ns::T 与 ns::Y
class U f(); // U 指代 ::U
Y* p; T* q; // 可使用到 T 和 Y 的指针及引用
}
#include
,而不是前置声明
建议: 倾向于使用#include
,而不是前置声明
原因:
// b.h:
struct B {};
struct D : B {};
// good_user.cc:
#include "b.h"
void f(B*);
void f(void*);
void test(D* x) { f(x); } // calls f(B*)
若把#include换成前置声明,由于声明时不知道D是B的子类,test()中f(x)就会导致f(void*)被调用,而不是f(B*)。
再比如,C++标准5.3.5/5中规定,delete一个不完整类型的指针时,如果这个类型有non-trivial
的析构函数,那么这种行为是未定义的。把前置声明换成#include则能保证消除这种风险。
什么时候该用前置声明,什么时候该用 #include