交叉引用的解決方法
什麼是交叉引用?
什麼是交叉引用?一言以蔽之,就是:A類中包含B類的物件,B類中包含A類的物件。
以一場景為例
我們先來看一個場景。假設有一個電子文件(Document)、一個文件下有多個頁(Page),每個頁下有多個文字單元(TextUnit,表示文件內元素的基本單位),一個文件中的所有文字單元物件都有唯一的ID。這樣每建立一個文字單元時都要為其設定一個唯一的ID,我們在Document類中就需要一個生成唯一ID的方法為所有的文字單元建立唯一標識。於是我們就會有下面的類關係設計圖:
圖1 :類的關係圖
於是我們想當然的會有這樣的程式碼:
TextUnit.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#pragma once class TextUnit { public: TextUnit(void); TextUnit(int id); ~TextUnit(void); public: int GetId() { return m_id; } void SetId(int id) { m_id = id; } private: int m_id; //文字物件的唯一標識 }; |
TextUnit.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include "StdAfx.h" #include "TextUnit.h" TextUnit::TextUnit(void) { } TextUnit::TextUnit( int id ) : m_id(id) { } TextUnit::~TextUnit(void) { } |
Page.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#pragma once #include <vector> #include "TextUnit.h" #include "Document.h" typedef std::vector<TextUnit*> VecTextUnit; class Page { public: Page(Document* pDocument); ~Page(void); public: //新增一個文字單元 TextUnit* AddTextUnit(); private: Document* m_pDocument; //文件物件,用於獲得ID VecTextUnit* m_pvecTextUnits; //文字單元物件 }; |
Page.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include "StdAfx.h" #include "Page.h" Page::Page(Document* pDocument) : m_pDocument(pDocument), m_pvecTextUnits(new VecTextUnit) { } Page::~Page(void) { for (VecTextUnit::iterator iter = m_pvecTextUnits->begin(); iter != m_pvecTextUnits->end(); ++ iter ) { delete[] *iter; *iter = NULL; } m_pvecTextUnits->clear(); delete[] m_pvecTextUnits; m_pvecTextUnits = NULL; } TextUnit* Page::AddTextUnit() { int id = m_pDocument->GenerateId(); TextUnit* pTextUnit = new TextUnit(id); if (pTextUnit == NULL) { return NULL; } m_pvecTextUnits->push_back(pTextUnit); return pTextUnit; } |
Document.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#pragma once #include <vector> #include "Page.h" typedef std::vector<Page*> VecPage; class Document { public: Document(void); ~Document(void); public: //生成本文件內唯一的文字物件ID int GenerateId(); //新增一頁 Page* AddPage(); private: static int s_id; //用於生成唯一的ID VecPage* m_pvecPages; //所有的頁 }; |
Document.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include "StdAfx.h" #include "Document.h" int Document::s_id = 0; Document::Document(void) : m_pvecPages(new VecPage) { } Document::~Document(void) { for (VecPage::iterator iter = m_pvecPages->begin(); iter != m_pvecPages->end(); ++ iter ) { delete[] *iter; *iter = NULL; } m_pvecPages->clear(); delete[] m_pvecPages; m_pvecPages = NULL; } int Document::GenerateId() { return s_id ++; } Page* Document::AddPage() { Page* pPage = new Page(this); if (pPage == NULL) { return NULL; } m_pvecPages->push_back(pPage); return pPage; } |
編譯
好,程式碼寫完了,我們對它進行編譯,這時你會發現一堆的錯誤:
1>d:部落格文章c++高階編輯projectscrossreferencecrossreferencedocument.h(6): error C2065: ‘Page’ : undeclared identifier
1>d:部落格文章c++高階編輯projectscrossreferencecrossreferencedocument.h(6): error C2059: syntax error : ‘>’
1>d:部落格文章c++高階編輯projectscrossreferencecrossreferencedocument.h(9): error C2143: syntax error : missing ‘;’ before ‘{’
…1>d:部落格文章c++高階編輯projectscrossreferencecrossreferencepage.h(12): error C2061: syntax error : identifier ‘Document’
1>d:部落格文章c++高階編輯projectscrossreferencecrossreferencepage.h(20): error C2143: syntax error : missing ‘;’ before ‘*’
1>d:部落格文章c++高階編輯projectscrossreferencecrossreferencepage.h(20): error C4430: missing type specifier – int assumed. Note: C++ does not support default-int
1>d:部落格文章c++高階編輯projectscrossreferencecrossreferencepage.h(20): error C4430: missing type specifier – int assumed. Note: C++ does not support default-int
…
原因分析
這是因為
1. C++中,在建立或使用一個類時,這個類必須被定義完整(就是一個完整的型別);
2. 類的定義也可以和函式一樣分成兩步,先宣告後定義。
1 |
class T; //宣告一個類 |
這種宣告也被稱為前向宣告,在程式中引入名字T,並指明T是一種類型別。此時的T在它被定義之前是不完整的型別(incomplete type),也就是說只知道它是一種類型別,並不知道它有哪些成員,但可定義這個型別(T)的指標和引用。
類的定義:
1 2 3 4 |
class T { // todo: 定義類的成員(屬性和方法) }; |
只有類(T)定義完成,它才是一個完整的型別,才是可見的(才可被建立和使用)。
而我們的程式現在就出現這種很有意思的狀態:在定義Document時,發現Page還沒有定義完整(Document中有Page型別的成員);在定義Page的時候發現Document還沒有定義完整(Page中有Document型別的指標物件)。也就是說Document不知道Page,Page不知道Docunent,這時就像兩隻狗打架,A狗咬著B狗的尾巴,B狗咬著A狗的尾巴。
解決方法
1.在Document.h文字中加入Page類的宣告:calss Page; 把include “Page.h”放到Document.cpp中。
2.Page.h文字中加入Page類的宣告:calss Document; 把include “Document.h”放到Page.h中。
程式碼如下:
Document.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#pragma once #include <vector> class Page; typedef std::vector<Page*> VecPage; class Document { public: Document(void); ~Document(void); public: //生成本文件內唯一的文字物件ID int GenerateId(); //新增一頁 Page* AddPage(); private: static int s_id; //用於生成唯一的ID VecPage* m_pvecPages; //所有的頁 }; |
Document.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include "StdAfx.h" #include "Document.h" #include "Page.h" int Document::s_id = 0; Document::Document(void) : m_pvecPages(new VecPage) { } Document::~Document(void) { for (VecPage::iterator iter = m_pvecPages->begin(); iter != m_pvecPages->end(); ++ iter ) { delete[] *iter; *iter = NULL; } m_pvecPages->clear(); delete[] m_pvecPages; m_pvecPages = NULL; } int Document::GenerateId() { return s_id ++; } Page* Document::AddPage() { Page* pPage = new Page(this); if (pPage == NULL) { return NULL; } m_pvecPages->push_back(pPage); return pPage; } |
Page.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#pragma once #include <vector> #include "TextUnit.h" class Document; typedef std::vector<TextUnit*> VecTextUnit; class Page { public: Page(Document* pDocument); ~Page(void); public: //新增一個文字單元 TextUnit* AddTextUnit(); private: Document* m_pDocument; //文件物件,用於獲得ID VecTextUnit* m_pvecTextUnits; //文字單元物件 }; |
Page.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include "StdAfx.h" #include "Page.h" #include "Document.h" Page::Page(Document* pDocument) : m_pDocument(pDocument), m_pvecTextUnits(new VecTextUnit) { } Page::~Page(void) { for (VecTextUnit::iterator iter = m_pvecTextUnits->begin(); iter != m_pvecTextUnits->end(); ++ iter ) { delete[] *iter; *iter = NULL; } m_pvecTextUnits->clear(); delete[] m_pvecTextUnits; m_pvecTextUnits = NULL; } TextUnit* Page::AddTextUnit() { int id = m_pDocument->GenerateId(); TextUnit* pTextUnit = new TextUnit(id); if (pTextUnit == NULL) { return NULL; } m_pvecTextUnits->push_back(pTextUnit); return pTextUnit; } |