相當於class 類名。
那么他和#include 包含頭文件有什么區別呢
首先我們為什么要包括頭文件問題的回答很簡單通常是我們需要獲得某個
類型的定義(definition)。那么接下來的問題就是在什么情況下我們才需要類
型的定義在什么情況下我們只需要聲明就足夠了問題的回答是當我們需要知
道這個類型的大小或者需要知道它的函數簽名的時候我們就需要獲得它的定
義。
假設我們有類型A和類型C在哪些情況下在A需要C的定義
1.A繼承至C
2.A有一個類型為C的成員變量
3.A有一個類型為C的指針的成員變量
4.A有一個類型為C的引用的成員變量
5.A有一個類型為std::list<C>的成員變量
6.A有一個函數它的簽名中參數和返回值都是類型C
7.A有一個函數它的簽名中參數和返回值都是類型C它調用了C的某個函數
代碼在頭文件中
8.A有一個函數它的簽名中參數和返回值都是類型C(包括類型C本身C的引
用類型和C的指針類型)並且它會調用另外一個使用C的函數代碼直接寫在
A的頭文件中
9.C和A在同一個名字空間里面
10.C和A在不同的名字空間里面
1沒有任何辦法必須要獲得C的定義因為我們必須要知道C的成員變量
成員函數。
2需要C的定義因為我們要知道C的大小來確定A的大小但是可以使用Pimpl
慣用法來改善這一點詳情請
看Hurb的Exceptional C++。
34不需要前置聲明就可以了其實3和4是一樣的引用在物理上也是一
個指針它的大小根據平台不同可能是32位也可能是64位反正我們不需要
知道C的定義就可以確定這個成員變量的大小。
5不需要有可能老式的編譯器需要。標准庫里面的容器像list vector
map
在包括一個list<C>vector<C>map<C, C>類型的成員變量的時候都不需要
C的定義。因為它們內部其實也是使用C的指針作為成員變量它們的大小一開
始就是固定的了不會根據模版參數的不同而改變。
6不需要只要我們沒有使用到C。
7需要我們需要知道調用函數的簽名。
88的情況比較復雜直接看代碼會比較清楚一些。
C& doToC(C&);
C& doToC2(C& c) ...{return doToC(c);};
從上面的代碼來看A的一個成員函數doToC2調用了另外一個成員函數doToC
但是無論是doToC2還是doToC它們的的參數和返回類型其實都是C的引用(換
成指針情況也一樣)引用的賦值跟指針的賦值都是一樣無非就是整形的賦
值所以這里即不需要知道C的大小也沒有調用C的任何函數實際上這里並不
需要C的定義。
但是我們隨便把其中一個C&換成C比如像下面的幾種示例
1.
C& doToC(C&);
C& doToC2(C c) ...{return doToC(c);};
2.
C& doToC(C);
C& doToC2(C& c) {return doToC(c);};
3.
C doToC(C&);
C& doToC2(C& c) {return doToC(c);};
4.
C& doToC(C&);
C doToC2(C& c) {return doToC(c);};
無論哪一種其實都隱式包含了一個拷貝構造函數的調用比如1中參數c由拷
貝構造函數生成3中doToC的返回值是一個由拷貝構造函數生成的匿名對象。
因為我們調用了C的拷貝構造函數所以以上無論那種情形都需要知道C的定義。
9和10都一樣我們都不需要知道C的定義只是10的情況下前置聲明的語
法會稍微復雜一些。
最后給出一個完整的例子我們可以看到在兩個不同名字空間的類型A和CA
是如何使用前置聲明來取代直接包括C的頭文件的
A.h
#pragma once
#include <list>
#include <vector>
#include <map>
#include <utility>
//不同名字空間的前置聲明方式
namespace test1
...{
class C;
}
namespace test2
...{
//用using避免使用完全限定名
using test1::C;
class A
...{
public:
C useC(C);
C& doToC(C&);
C& doToC2(C& c) ...{return doToC(c);};
private:
std::list<C> _list;
std::vector<C> _vector;
std::map<C, C> _map;
C* _pc;
C& _rc;
};
}
C.h
#ifndef C_H
#define C_H
#include <iostream>
namespace test1
...{
class C
...{
public:
void print() ...{std::cout<<"Class C"<<std::endl;}
};
}
#endif // C_H
