交叉引用的解決方法
什麼是交叉引用?
什麼是交叉引用?一言以蔽之,就是:A類中包括B類的對象。B類中包括A類的對象。
以一場景為例
我們先來看一個場景。如果有一個電子文檔(Document)、一個文檔下有多個頁(Page),每一個頁下有多個文本單元(TextUnit,表示文檔内元素的基本機關),一個文檔中的全部文本單元對象都有唯一的ID。這樣每建立一個文本單元時都要為其設定一個唯一的ID。我們在Document類中就須要一個生成唯一ID的方法為全部的文本單元建立唯一辨別。于是我們就會有以下的類關系設計圖:
圖1 :類的關系圖
于是我們想當然的會有這樣的代碼:
TextUnit.h:
#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:
#include "StdAfx.h"
#include "TextUnit.h"
TextUnit::TextUnit(void)
{
}
TextUnit::TextUnit( int id ) : m_id(id)
{
}
TextUnit::~TextUnit(void)
{
}
Page.h:
#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:
#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:
#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:
#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++進階編輯\projects\crossreference\crossreference\document.h(6): error C2065: ‘Page’ : undeclared identifier
1>d:\部落格文章\c++進階編輯\projects\crossreference\crossreference\document.h(6): error C2059: syntax error : ‘>’
1>d:\部落格文章\c++進階編輯\projects\crossreference\crossreference\document.h(9): error C2143: syntax error : missing ‘;’ before ‘{’
…
1>d:\部落格文章\c++進階編輯\projects\crossreference\crossreference\page.h(12): error C2061: syntax error : identifier ‘Document’
1>d:\部落格文章\c++進階編輯\projects\crossreference\crossreference\page.h(20): error C2143: syntax error : missing ‘;’ before ‘*’
1>d:\部落格文章\c++進階編輯\projects\crossreference\crossreference\page.h(20): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
原因分析
這是由于
1. C++中,在建立或使用一個類時。這個類必須被定義完整(就是一個完整的類型);
2. 類的定義也能夠和函數一樣分成兩步,先聲明後定義。
class T; //聲明一個類
這樣的聲明也被稱為前向聲明。在程式中引入名字T。并指明T是一種類類型。此時的T在它被定義之前是不完整的類型(incomplete type),也就是說僅僅知道它是一種類類型,并不知道它有哪些成員,但可定義這個類型(T)的指針和引用。
類的定義:
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中。
代碼例如以下:
#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; //全部的頁
};
#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;
}
#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; //文本單元對象
};
#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;
}